<!DOCTYPE html>


<html lang="zh-CN">


<head>
  <meta charset="utf-8" />
  <meta name="baidu-site-verification" content="code-kg5UjKJZM2" />
   
  <meta name="keywords" content="活,炼" />
   
  <meta name="description" content="shimmerjordan" />
  
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
  <title>
    SpringBoot2 扎记上篇 |  丛烨-shimmerjordan
  </title>
  <meta name="generator" content="hexo-theme-ayer">
  
  <link rel="shortcut icon" href="/favicon.ico" />
  
  
<link rel="stylesheet" href="/dist/main.css">

  <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/Shen-Yu/cdn/css/remixicon.min.css">
  
<link rel="stylesheet" href="/css/custom.css">

  
  <script src="https://cdn.jsdelivr.net/npm/pace-js@1.0.2/pace.min.js"></script>
  
  

<script type="text/javascript">
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');

ga('create', 'G-Q0DT8B8VJW', 'auto');
ga('send', 'pageview');

</script>



  
<script>
var _hmt = _hmt || [];
(function() {
	var hm = document.createElement("script");
	hm.src = "https://hm.baidu.com/hm.js?6d06f826e125297d4ce0fa7a1449328e";
	var s = document.getElementsByTagName("script")[0]; 
	s.parentNode.insertBefore(hm, s);
})();
</script>


<link rel="alternate" href="/atom.xml" title="丛烨-shimmerjordan" type="application/atom+xml">
</head>

</html>

	<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/font-awesome/css/font-awesome.min.css">
	<script src="https://cdn.jsdelivr.net/gh/stevenjoezhang/live2d-widget@latest/autoload.js"></script>


<body>
  <div id="app">
    
      
    <main class="content on">
      <section class="outer">
  <article
  id="post-SpringBoot2-part1"
  class="article article-type-post"
  itemscope
  itemprop="blogPost"
  data-scroll-reveal
>
  <div class="article-inner">
    
    <header class="article-header">
       
<h1 class="article-title sea-center" style="border-left:0" itemprop="name">
  SpringBoot2 扎记上篇
</h1>
 

    </header>
     
    <div class="article-meta">
      <a href="/2022/07/03/Spring-boot-dict-1/" class="article-date">
  <time datetime="2022-07-03T12:36:12.000Z" itemprop="datePublished">2022-07-03</time>
</a> 
  <div class="article-category">
    <a class="article-category-link" href="/categories/SpringBoot/">SpringBoot</a>
  </div>
  
<div class="word_count">
    <span class="post-time">
        <span class="post-meta-item-icon">
            <i class="ri-quill-pen-line"></i>
            <span class="post-meta-item-text"> 字数统计:</span>
            <span class="post-count">19.4k</span>
        </span>
    </span>

    <span class="post-time">
        &nbsp; | &nbsp;
        <span class="post-meta-item-icon">
            <i class="ri-book-open-line"></i>
            <span class="post-meta-item-text"> 阅读时长≈</span>
            <span class="post-count">99 分钟</span>
        </span>
    </span>
</div>
 
    </div>
      
    <div class="tocbot"></div>




  
    <div class="article-entry" itemprop="articleBody">
       
  <h1 id="SpringBoot2笔记上篇"><a href="#SpringBoot2笔记上篇" class="headerlink" title="SpringBoot2笔记上篇"></a>SpringBoot2笔记上篇</h1><h1 id="基础入门-SpringBoot2"><a href="#基础入门-SpringBoot2" class="headerlink" title="基础入门-SpringBoot2"></a>基础入门-SpringBoot2</h1><ol>
<li><p>Spring Boot 2核心技术</p>
</li>
<li><p>Spring Boot 2响应式编程</p>
</li>
</ol>
<ul>
<li>环境要求<ul>
<li>Java8及以上</li>
<li>Maven 3.3及以上</li>
</ul>
</li>
<li>学习资料<ul>
<li><a target="_blank" rel="noopener" href="https://spring.io/projects/spring-boot">Spring Boot官网</a></li>
<li><a target="_blank" rel="noopener" href="https://docs.spring.io/spring-boot/docs/">Spring Boot官方文档</a></li>
</ul>
</li>
</ul>
<span id="more"></span>
<h2 id="Spring生态圈"><a href="#Spring生态圈" class="headerlink" title="Spring生态圈"></a>Spring生态圈</h2><p><a target="_blank" rel="noopener" href="https://spring.io/">Spring官网</a></p>
<h2 id="Spring能做什么"><a href="#Spring能做什么" class="headerlink" title="Spring能做什么"></a>Spring能做什么</h2><h3 id="Spring的能力"><a href="#Spring的能力" class="headerlink" title="Spring的能力"></a>Spring的能力</h3><center>
    <img style="border-radius: 0.3125em;
    box-shadow: 0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.08);" 
    src="https://gcore.jsdelivr.net/gh/shimmerjordan/pic_bed@main/blog/SpringBoot2/image/20210205004146543.png" width="70%">
    <br>
    <div style="color:orange; border-bottom: 1px solid #d9d9d9;
    display: inline-block;
    color: #999;
    padding: 2px;">Spring的能力</div>
</center>

<h3 id="Spring的生态"><a href="#Spring的生态" class="headerlink" title="Spring的生态"></a>Spring的生态</h3><p>覆盖了：</p>
<ul>
<li>web开发</li>
<li>数据访问</li>
<li>安全控制</li>
<li>分布式</li>
<li>消息服务</li>
<li>移动开发</li>
<li>批处理</li>
<li>……</li>
</ul>
<h3 id="Spring5重大升级"><a href="#Spring5重大升级" class="headerlink" title="Spring5重大升级"></a>Spring5重大升级</h3><ul>
<li>响应式编程</li>
</ul>
<center>
    <img style="border-radius: 0.3125em;
    box-shadow: 0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.08);" 
    src="https://gcore.jsdelivr.net/gh/shimmerjordan/pic_bed@main/blog/SpringBoot2/image/20210205004250581.png" width="70%">
    <br>
    <div style="color:orange; border-bottom: 1px solid #d9d9d9;
    display: inline-block;
    color: #999;
    padding: 2px;">响应式编程</div>
</center>

<ul>
<li>内部源码设计</li>
</ul>
<p>基于Java8的一些新特性，如：接口默认实现。重新设计源码架构。</p>
<h2 id="为什么用SpringBoot"><a href="#为什么用SpringBoot" class="headerlink" title="为什么用SpringBoot"></a>为什么用SpringBoot</h2><blockquote>
<p>Spring Boot makes it easy to create stand-alone, production-grade Spring based Applications that you can “just run”.<a target="_blank" rel="noopener" href="https://spring.io/projects/spring-boot">link</a></p>
<p>能快速创建出生产级别的Spring应用。</p>
</blockquote>
<h3 id="SpringBoot优点"><a href="#SpringBoot优点" class="headerlink" title="SpringBoot优点"></a>SpringBoot优点</h3><ul>
<li><p>Create stand-alone Spring applications</p>
<ul>
<li>创建独立Spring应用</li>
</ul>
</li>
<li><p>Embed Tomcat, Jetty or Undertow directly (no need to deploy WAR files)</p>
<ul>
<li>内嵌web服务器</li>
</ul>
</li>
<li><p>Provide opinionated ‘starter’ dependencies to simplify your build configuration</p>
<ul>
<li>自动starter依赖，简化构建配置</li>
</ul>
</li>
<li><p>Automatically configure Spring and 3rd party libraries whenever possible</p>
<ul>
<li>自动配置Spring以及第三方功能</li>
</ul>
</li>
<li><p>Provide production-ready features such as metrics, health checks, and externalized configuration</p>
<ul>
<li>提供生产级别的监控、健康检查及外部化配置</li>
</ul>
</li>
<li><p>Absolutely no code generation and no requirement for XML configuration</p>
<ul>
<li>无代码生成、无需编写XML</li>
</ul>
</li>
<li><p>SpringBoot是整合Spring技术栈的一站式框架</p>
</li>
<li><p>SpringBoot是简化Spring技术栈的快速开发脚手架</p>
</li>
</ul>
<h3 id="SpringBoot缺点"><a href="#SpringBoot缺点" class="headerlink" title="SpringBoot缺点"></a>SpringBoot缺点</h3><ul>
<li>人称版本帝，迭代快，需要时刻关注变化</li>
<li>封装太深，内部原理复杂，不容易精通</li>
</ul>
<h2 id="SpringBoot的大时代背景"><a href="#SpringBoot的大时代背景" class="headerlink" title="SpringBoot的大时代背景"></a>SpringBoot的大时代背景</h2><h3 id="微服务"><a href="#微服务" class="headerlink" title="微服务"></a>微服务</h3><blockquote>
<p>In short, the <strong>microservice architectural style</strong> is an approach to developing a single application as a <strong>suite of small services</strong>, each <strong>running in its own process</strong> and communicating with <strong>lightweight</strong> mechanisms, often an <strong>HTTP</strong> resource API. These services are built around <strong>business capabilities</strong> and <strong>independently deployable</strong> by fully <strong>automated deployment</strong> machinery. There is a bare minimum of centralized management of these services, which may be <strong>written in different programming languages</strong> and use different data storage technologies.——<a target="_blank" rel="noopener" href="https://martinfowler.com/articles/microservices.html">James Lewis and Martin Fowler (2014)</a></p>
</blockquote>
<ul>
<li>微服务是一种架构风格</li>
<li>一个应用拆分为一组小型服务</li>
<li>每个服务运行在自己的进程内，也就是可独立部署和升级</li>
<li>服务之间使用轻量级HTTP交互</li>
<li>服务围绕业务功能拆分</li>
<li>可以由全自动部署机制独立部署</li>
<li>去中心化，服务自治。服务可以使用不同的语言、不同的存储技术</li>
</ul>
<h3 id="分布式"><a href="#分布式" class="headerlink" title="分布式"></a>分布式</h3><center>
    <img style="border-radius: 0.3125em;
    box-shadow: 0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.08);" 
    src="https://gcore.jsdelivr.net/gh/shimmerjordan/pic_bed@main/blog/SpringBoot2/image/2021020500434620.png" width="70%">
    <br>
    <div style="color:orange; border-bottom: 1px solid #d9d9d9;
    display: inline-block;
    color: #999;
    padding: 2px;">微服务</div>
</center>

<h4 id="分布式的困难"><a href="#分布式的困难" class="headerlink" title="分布式的困难"></a>分布式的困难</h4><ul>
<li>远程调用</li>
<li>服务发现</li>
<li>负载均衡</li>
<li>服务容错</li>
<li>配置管理</li>
<li>服务监控</li>
<li>链路追踪</li>
<li>日志管理</li>
<li>任务调度</li>
<li>……</li>
</ul>
<h4 id="分布式的解决"><a href="#分布式的解决" class="headerlink" title="分布式的解决"></a>分布式的解决</h4><ul>
<li>SpringBoot + SpringCloud</li>
</ul>
<center>
    <img style="border-radius: 0.3125em;
    box-shadow: 0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.08);" 
    src="https://gcore.jsdelivr.net/gh/shimmerjordan/pic_bed@main/blog/SpringBoot2/image/20210205004523307.png" width="70%">
    <br>
    <div style="color:orange; border-bottom: 1px solid #d9d9d9;
    display: inline-block;
    color: #999;
    padding: 2px;">分布式解决方案</div>
</center>

<h3 id="云原生"><a href="#云原生" class="headerlink" title="云原生"></a>云原生</h3><p>原生应用如何上云。 Cloud Native</p>
<p><strong>上云的困难</strong></p>
<ul>
<li>服务自愈</li>
<li>弹性伸缩</li>
<li>服务隔离</li>
<li>自动化部署</li>
<li>灰度发布</li>
<li>流量治理</li>
<li>……</li>
</ul>
<h2 id="SpringBoot官方文档架构"><a href="#SpringBoot官方文档架构" class="headerlink" title="SpringBoot官方文档架构"></a>SpringBoot官方文档架构</h2><ul>
<li><a target="_blank" rel="noopener" href="https://spring.io/projects/spring-boot">Spring Boot官网</a></li>
<li><a target="_blank" rel="noopener" href="https://docs.spring.io/spring-boot/docs/">Spring Boot官方文档</a></li>
</ul>
<p><strong>官网文档架构</strong></p>
<center>
    <img style="border-radius: 0.3125em;
    box-shadow: 0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.08);" 
    src="https://gcore.jsdelivr.net/gh/shimmerjordan/pic_bed@main/blog/SpringBoot2/image/20210205004733270.png" width="100%">
    <br>
    <div style="color:orange; border-bottom: 1px solid #d9d9d9;
    display: inline-block;
    color: #999;
    padding: 2px;">文档架构</div>
</center>

<center>
    <img style="border-radius: 0.3125em;
    box-shadow: 0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.08);" 
    src="https://gcore.jsdelivr.net/gh/shimmerjordan/pic_bed@main/blog/SpringBoot2/image/20210205004828702.png" width="100%">
    <br>
    <div style="color:orange; border-bottom: 1px solid #d9d9d9;
    display: inline-block;
    color: #999;
    padding: 2px;">官网文档架构</div>
</center>

<p><a target="_blank" rel="noopener" href="https://github.com/spring-projects/spring-boot/wiki#release-notes">查看版本新特性</a></p>
<center>
    <img style="border-radius: 0.3125em;
    box-shadow: 0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.08);" 
    src="https://gcore.jsdelivr.net/gh/shimmerjordan/pic_bed@main/blog/SpringBoot2/image/20210205005342147.png" width="90%">
    <br>
    <div style="color:orange; border-bottom: 1px solid #d9d9d9;
    display: inline-block;
    color: #999;
    padding: 2px;">新特性</div>
</center>

<h1 id="SpringBoot-HelloWorld"><a href="#SpringBoot-HelloWorld" class="headerlink" title="SpringBoot-HelloWorld"></a>SpringBoot-HelloWorld</h1><h2 id="Maven配置文件"><a href="#Maven配置文件" class="headerlink" title="Maven配置文件"></a>Maven配置文件</h2><p>新添内容：</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">mirrors</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">mirror</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">id</span>&gt;</span>nexus-aliyun<span class="tag">&lt;/<span class="name">id</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">mirrorOf</span>&gt;</span>central<span class="tag">&lt;/<span class="name">mirrorOf</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">name</span>&gt;</span>Nexus aliyun<span class="tag">&lt;/<span class="name">name</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">url</span>&gt;</span>http://maven.aliyun.com/nexus/content/groups/public<span class="tag">&lt;/<span class="name">url</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">mirror</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">mirrors</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">profiles</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">profile</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">id</span>&gt;</span>jdk-1.8<span class="tag">&lt;/<span class="name">id</span>&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="tag">&lt;<span class="name">activation</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">activeByDefault</span>&gt;</span>true<span class="tag">&lt;/<span class="name">activeByDefault</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">jdk</span>&gt;</span>1.8<span class="tag">&lt;/<span class="name">jdk</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">activation</span>&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="tag">&lt;<span class="name">properties</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">maven.compiler.source</span>&gt;</span>1.8<span class="tag">&lt;/<span class="name">maven.compiler.source</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">maven.compiler.target</span>&gt;</span>1.8<span class="tag">&lt;/<span class="name">maven.compiler.target</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">maven.compiler.compilerVersion</span>&gt;</span>1.8<span class="tag">&lt;/<span class="name">maven.compiler.compilerVersion</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">properties</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">profile</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">profiles</span>&gt;</span></span><br></pre></td></tr></table></figure>
<p>需求：浏览发送<code>/hello</code>请求，响应 <code>Hello，Spring Boot 2</code></p>
<h2 id="创建maven工程"><a href="#创建maven工程" class="headerlink" title="创建maven工程"></a>创建maven工程</h2><h3 id="引入依赖"><a href="#引入依赖" class="headerlink" title="引入依赖"></a>引入依赖</h3><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">parent</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-parent<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>2.3.4.RELEASE<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">parent</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">dependencies</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-web<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependencies</span>&gt;</span></span><br></pre></td></tr></table></figure>
<h3 id="创建主程序"><a href="#创建主程序" class="headerlink" title="创建主程序"></a>创建主程序</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> org.springframework.boot.SpringApplication;</span><br><span class="line"><span class="keyword">import</span> org.springframework.boot.autoconfigure.SpringBootApplication;</span><br><span class="line"></span><br><span class="line"><span class="meta">@SpringBootApplication</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MainApplication</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        SpringApplication.run(MainApplication.class, args);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h3 id="编写业务"><a href="#编写业务" class="headerlink" title="编写业务"></a>编写业务</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> org.springframework.web.bind.annotation.RequestMapping;</span><br><span class="line"><span class="keyword">import</span> org.springframework.web.bind.annotation.RestController;</span><br><span class="line"></span><br><span class="line"><span class="meta">@RestController</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">HelloController</span> &#123;</span><br><span class="line">    <span class="meta">@RequestMapping(&quot;/hello&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">handle01</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;Hello, Spring Boot 2!&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h3 id="运行-amp-测试"><a href="#运行-amp-测试" class="headerlink" title="运行&amp;测试"></a>运行&amp;测试</h3><ul>
<li>运行<code>MainApplication</code>类</li>
<li>浏览器输入<code>http://localhost:8888/hello</code>，将会输出<code>Hello, Spring Boot 2!</code>。</li>
</ul>
<h3 id="设置配置"><a href="#设置配置" class="headerlink" title="设置配置"></a>设置配置</h3><p>maven工程的resource文件夹中创建application.properties文件。</p>
<figure class="highlight properties"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 设置端口号</span></span><br><span class="line"><span class="attr">server.port</span>=<span class="string">8888</span></span><br></pre></td></tr></table></figure>
<p><a target="_blank" rel="noopener" href="https://docs.spring.io/spring-boot/docs/2.3.7.RELEASE/reference/html/appendix-application-properties.html#common-application-properties-server">更多配置信息</a></p>
<h3 id="打包部署"><a href="#打包部署" class="headerlink" title="打包部署"></a>打包部署</h3><p>在pom.xml添加</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">build</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">plugins</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">plugin</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-maven-plugin<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">plugin</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">plugins</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">build</span>&gt;</span></span><br></pre></td></tr></table></figure>
<p>在IDEA的Maven插件上点击运行 clean 、package，把helloworld工程项目的打包成jar包，</p>
<p>打包好的jar包被生成在helloworld工程项目的target文件夹内。</p>
<p>用cmd运行<code>java -jar boot-01-helloworld-1.0-SNAPSHOT.jar</code>，既可以运行helloworld工程项目。</p>
<p>将jar包直接在目标服务器执行即可。</p>
<h1 id="SpringBoot-依赖管理特性"><a href="#SpringBoot-依赖管理特性" class="headerlink" title="SpringBoot-依赖管理特性"></a>SpringBoot-依赖管理特性</h1><ul>
<li>父项目做依赖管理</li>
</ul>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">parent</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-parent<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>2.3.4.RELEASE<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">parent</span>&gt;</span></span><br></pre></td></tr></table></figure>
<p>上面项目的父项目如下：</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">parent</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-dependencies<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>2.3.4.RELEASE<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">parent</span>&gt;</span></span><br></pre></td></tr></table></figure>
<p>它几乎声明了所有开发中常用的依赖的版本号，自动版本仲裁机制</p>
<ul>
<li>开发导入starter场景启动器<ol>
<li>见到很多 spring-boot-starter-<em> ： </em>就某种场景</li>
<li>只要引入starter，这个场景的所有常规需要的依赖我们都自动引入</li>
<li><a target="_blank" rel="noopener" href="https://docs.spring.io/spring-boot/docs/current/reference/html/using-spring-boot.html#using-boot-starter">更多SpringBoot所有支持的场景</a></li>
<li>见到的  *-spring-boot-starter： 第三方为我们提供的简化开发的场景启动器。</li>
</ol>
</li>
</ul>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">所有场景启动器最底层的依赖</span><br><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>2.3.4.RELEASE<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">scope</span>&gt;</span>compile<span class="tag">&lt;/<span class="name">scope</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure>
<ul>
<li><p>无需关注版本号，自动版本仲裁</p>
<ol>
<li>引入依赖默认都可以不写版本</li>
<li>引入非版本仲裁的jar，要写版本号。</li>
</ol>
</li>
<li><p>可以修改默认版本号</p>
<ol>
<li>查看spring-boot-dependencies里面规定当前依赖的版本用的 key。</li>
<li>在当前项目里面重写配置，如下面的代码。</li>
</ol>
</li>
</ul>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">properties</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">mysql.version</span>&gt;</span>5.1.43<span class="tag">&lt;/<span class="name">mysql.version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">properties</span>&gt;</span></span><br></pre></td></tr></table></figure>
<hr>
<p>IDEA快捷键：</p>
<ul>
<li><code>ctrl + shift + alt + U</code>：以图的方式显示项目中依赖之间的关系。</li>
<li><code>alt + ins</code>：相当于Eclipse的 Ctrl + N，创建新类，新包等。</li>
</ul>
<h1 id="SpringBoot-自动配置特性"><a href="#SpringBoot-自动配置特性" class="headerlink" title="SpringBoot-自动配置特性"></a><a id="SpringBootAutoConfig">SpringBoot-自动配置特性</a></h1><ul>
<li>自动配好Tomcat<ul>
<li>引入Tomcat依赖。</li>
<li>配置Tomcat</li>
</ul>
</li>
</ul>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-tomcat<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>2.3.4.RELEASE<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">scope</span>&gt;</span>compile<span class="tag">&lt;/<span class="name">scope</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure>
<ul>
<li><p>自动配好SpringMVC</p>
<ul>
<li>引入SpringMVC全套组件</li>
<li>自动配好SpringMVC常用组件（功能）</li>
</ul>
</li>
<li><p>自动配好Web常见功能，如：字符编码问题</p>
<ul>
<li>SpringBoot帮我们配置好了<strong>所有web开发的常见场景</strong></li>
</ul>
</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">    <span class="comment">//1、返回我们IOC容器</span></span><br><span class="line">    <span class="type">ConfigurableApplicationContext</span> <span class="variable">run</span> <span class="operator">=</span> SpringApplication.run(MainApplication.class, args);</span><br><span class="line"></span><br><span class="line">    <span class="comment">//2、查看容器里面的组件</span></span><br><span class="line">    String[] names = run.getBeanDefinitionNames();</span><br><span class="line">    <span class="keyword">for</span> (String name : names) &#123;</span><br><span class="line">        System.out.println(name);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<ul>
<li>默认的包结构<ul>
<li>主程序所在包及其下面的所有子包里面的组件都会被默认扫描进来</li>
<li>无需以前的包扫描配置</li>
<li>想要改变扫描路径<ul>
<li><code>@SpringBootApplication(scanBasePackages=&quot;com.lun&quot;)</code></li>
<li><code>@ComponentScan</code> 指定扫描路径</li>
</ul>
</li>
</ul>
</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@SpringBootApplication</span></span><br><span class="line"><span class="comment">// 等同于</span></span><br><span class="line"><span class="meta">@SpringBootConfiguration</span></span><br><span class="line"><span class="meta">@EnableAutoConfiguration</span></span><br><span class="line"><span class="meta">@ComponentScan(&quot;com.lun&quot;)</span></span><br></pre></td></tr></table></figure>
<ul>
<li><p>各种配置拥有默认值</p>
<ul>
<li>默认配置最终都是映射到某个类上，如：<code>MultipartProperties</code></li>
<li>配置文件的值最终会绑定每个类上，这个类会在容器中创建对象</li>
</ul>
</li>
<li><p>按需加载所有自动配置项</p>
<ul>
<li>非常多的starter</li>
<li>引入了哪些场景这个场景的自动配置才会开启</li>
<li>SpringBoot所有的自动配置功能都在 <code>spring-boot-autoconfigure</code> 包里面</li>
</ul>
</li>
</ul>
<h1 id="底层注解"><a href="#底层注解" class="headerlink" title="底层注解"></a>底层注解</h1><h2 id="Configuration详解"><a href="#Configuration详解" class="headerlink" title="@Configuration详解"></a>@Configuration详解</h2><ul>
<li>基本使用<ul>
<li>Full模式与Lite模式</li>
<li>示例</li>
</ul>
</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 1、配置类里面使用<span class="doctag">@Bean</span>标注在方法上给容器注册组件，默认也是单实例的</span></span><br><span class="line"><span class="comment"> * 2、配置类本身也是组件</span></span><br><span class="line"><span class="comment"> * 3、proxyBeanMethods：代理bean的方法</span></span><br><span class="line"><span class="comment"> *      Full(proxyBeanMethods = true)（保证每个<span class="doctag">@Bean</span>方法被调用多少次返回的组件都是单实例的）（默认）</span></span><br><span class="line"><span class="comment"> *      Lite(proxyBeanMethods = false)（每个<span class="doctag">@Bean</span>方法被调用多少次返回的组件都是新创建的）</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Configuration(proxyBeanMethods = false)</span> <span class="comment">//告诉SpringBoot这是一个配置类 == 配置文件</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyConfig</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Full:外部无论对配置类中的这个组件注册方法调用多少次获取的都是之前注册容器中的单实例对象</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Bean</span> <span class="comment">//给容器中添加组件。以方法名作为组件的id。返回类型就是组件类型。返回的值，就是组件在容器中的实例</span></span><br><span class="line">    <span class="keyword">public</span> User <span class="title function_">user01</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="type">User</span> <span class="variable">zhangsan</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">User</span>(<span class="string">&quot;zhangsan&quot;</span>, <span class="number">18</span>);</span><br><span class="line">        <span class="comment">//user组件依赖了Pet组件</span></span><br><span class="line">        zhangsan.setPet(tomcatPet());</span><br><span class="line">        <span class="keyword">return</span> zhangsan;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Bean(&quot;tom&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Pet <span class="title function_">tomcatPet</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Pet</span>(<span class="string">&quot;tomcat&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p><code>@Configuration</code>测试代码如下:</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@SpringBootConfiguration</span></span><br><span class="line"><span class="meta">@EnableAutoConfiguration</span></span><br><span class="line"><span class="meta">@ComponentScan(&quot;com.shimmer.boot&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MainApplication</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="comment">//1、返回我们IOC容器</span></span><br><span class="line">        <span class="type">ConfigurableApplicationContext</span> <span class="variable">run</span> <span class="operator">=</span> SpringApplication.run(MainApplication.class, args);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//2、查看容器里面的组件</span></span><br><span class="line">        String[] names = run.getBeanDefinitionNames();</span><br><span class="line">        <span class="keyword">for</span> (String name : names) &#123;</span><br><span class="line">            System.out.println(name);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//3、从容器中获取组件</span></span><br><span class="line">        <span class="type">Pet</span> <span class="variable">tom01</span> <span class="operator">=</span> run.getBean(<span class="string">&quot;tom&quot;</span>, Pet.class);</span><br><span class="line">        <span class="type">Pet</span> <span class="variable">tom02</span> <span class="operator">=</span> run.getBean(<span class="string">&quot;tom&quot;</span>, Pet.class);</span><br><span class="line">        System.out.println(<span class="string">&quot;组件：&quot;</span>+(tom01 == tom02));</span><br><span class="line"></span><br><span class="line">        <span class="comment">//4、com.shimmer.boot.config.MyConfig$$EnhancerBySpringCGLIB$$51f1e1ca@1654a892</span></span><br><span class="line">        <span class="type">MyConfig</span> <span class="variable">bean</span> <span class="operator">=</span> run.getBean(MyConfig.class);</span><br><span class="line">        System.out.println(bean);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//如果@Configuration(proxyBeanMethods = true)代理对象调用方法。SpringBoot总会检查这个组件是否在容器中有。</span></span><br><span class="line">        <span class="comment">//保持组件单实例</span></span><br><span class="line">        <span class="type">User</span> <span class="variable">user</span> <span class="operator">=</span> bean.user01();</span><br><span class="line">        <span class="type">User</span> <span class="variable">user1</span> <span class="operator">=</span> bean.user01();</span><br><span class="line">        System.out.println(user == user1);</span><br><span class="line"></span><br><span class="line">        <span class="type">User</span> <span class="variable">user01</span> <span class="operator">=</span> run.getBean(<span class="string">&quot;user01&quot;</span>, User.class);</span><br><span class="line">        <span class="type">Pet</span> <span class="variable">tom</span> <span class="operator">=</span> run.getBean(<span class="string">&quot;tom&quot;</span>, Pet.class);</span><br><span class="line"></span><br><span class="line">        System.out.println(<span class="string">&quot;用户的宠物：&quot;</span>+(user01.getPet() == tom));</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<ul>
<li>最佳实战<ul>
<li>配置类组件之间<strong>无依赖关系</strong>用Lite模式加速容器启动过程，减少判断</li>
<li>配置类组件之间<strong>有依赖关系</strong>，方法会被调用得到之前单实例组件，用Full模式（默认）</li>
</ul>
</li>
</ul>
<hr>
<p>IDEA快捷键：</p>
<ul>
<li><code>Alt + Ins</code>:生成getter，setter、构造器等代码。</li>
<li><code>Ctrl + Alt + B</code>:查看类的具体实现代码。</li>
</ul>
<h2 id="Import导入组件"><a href="#Import导入组件" class="headerlink" title="@Import导入组件"></a>@Import导入组件</h2><p><code>@Bean</code>、<code>@Component</code>、<code>@Controller</code>、<code>@Service</code>、<code>@Repository</code>，它们是Spring的基本标签，在Spring Boot中并未改变它们原来的功能。</p>
<p><code>@ComponentScan</code> 在<a href="#SpringBootAutoConfig">SpringBoot-自动配置特性</a>有用例。</p>
<p><code>@Import(&#123;User.class, DBHelper.class&#125;)</code>给容器中<strong>自动创建出这两个类型的组件</strong>，默认组件的名字就是全类名</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Import(&#123;User.class, DBHelper.class&#125;)</span></span><br><span class="line"><span class="meta">@Configuration(proxyBeanMethods = false)</span> <span class="comment">//告诉SpringBoot这是一个配置类 == 配置文件</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyConfig</span> &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>测试类：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//1、返回我们IOC容器</span></span><br><span class="line"><span class="type">ConfigurableApplicationContext</span> <span class="variable">run</span> <span class="operator">=</span> SpringApplication.run(MainApplication.class, args);</span><br><span class="line"></span><br><span class="line"><span class="comment">//...</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//5、获取组件</span></span><br><span class="line">String[] beanNamesForType = run.getBeanNamesForType(User.class);</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> (String s : beanNamesForType) &#123;</span><br><span class="line">    System.out.println(s);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">DBHelper</span> <span class="variable">bean1</span> <span class="operator">=</span> run.getBean(DBHelper.class);</span><br><span class="line">System.out.println(bean1);</span><br></pre></td></tr></table></figure>
<h2 id="Conditional条件装配"><a href="#Conditional条件装配" class="headerlink" title="@Conditional条件装配"></a>@Conditional条件装配</h2><p><strong>条件装配：满足Conditional指定的条件，则进行组件注入</strong></p>
<center>
    <img style="border-radius: 0.3125em;
    box-shadow: 0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.08);" 
    src="https://gcore.jsdelivr.net/gh/shimmerjordan/pic_bed@main/blog/SpringBoot2/image/20210205005453173.png" width="70%">
    <br>
    <div style="color:orange; border-bottom: 1px solid #d9d9d9;
    display: inline-block;
    color: #999;
    padding: 2px;">组件注入</div>
</center>

<p>用<code>@ConditionalOnMissingBean</code>举例说明</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Configuration(proxyBeanMethods = false)</span></span><br><span class="line"><span class="meta">@ConditionalOnMissingBean(name = &quot;tom&quot;)</span><span class="comment">//没有tom名字的Bean时，MyConfig类的Bean才能生效。</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyConfig</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> User <span class="title function_">user01</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="type">User</span> <span class="variable">zhangsan</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">User</span>(<span class="string">&quot;zhangsan&quot;</span>, <span class="number">18</span>);</span><br><span class="line">        zhangsan.setPet(tomcatPet());</span><br><span class="line">        <span class="keyword">return</span> zhangsan;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Bean(&quot;tom22&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Pet <span class="title function_">tomcatPet</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Pet</span>(<span class="string">&quot;tomcat&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">    <span class="comment">//1、返回我们IOC容器</span></span><br><span class="line">    <span class="type">ConfigurableApplicationContext</span> <span class="variable">run</span> <span class="operator">=</span> SpringApplication.run(MainApplication.class, args);</span><br><span class="line"></span><br><span class="line">    <span class="comment">//2、查看容器里面的组件</span></span><br><span class="line">    String[] names = run.getBeanDefinitionNames();</span><br><span class="line">    <span class="keyword">for</span> (String name : names) &#123;</span><br><span class="line">        System.out.println(name);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="type">boolean</span> <span class="variable">tom</span> <span class="operator">=</span> run.containsBean(<span class="string">&quot;tom&quot;</span>);</span><br><span class="line">    System.out.println(<span class="string">&quot;容器中Tom组件：&quot;</span>+tom);<span class="comment">//false</span></span><br><span class="line"></span><br><span class="line">    <span class="type">boolean</span> <span class="variable">user01</span> <span class="operator">=</span> run.containsBean(<span class="string">&quot;user01&quot;</span>);</span><br><span class="line">    System.out.println(<span class="string">&quot;容器中user01组件：&quot;</span>+user01);<span class="comment">//true</span></span><br><span class="line"></span><br><span class="line">    <span class="type">boolean</span> <span class="variable">tom22</span> <span class="operator">=</span> run.containsBean(<span class="string">&quot;tom22&quot;</span>);</span><br><span class="line">    System.out.println(<span class="string">&quot;容器中tom22组件：&quot;</span>+tom22);<span class="comment">//true</span></span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="ImportResource导入Spring配置文件"><a href="#ImportResource导入Spring配置文件" class="headerlink" title="@ImportResource导入Spring配置文件"></a>@ImportResource导入Spring配置文件</h2><p>比如，公司使用<code>bean.xml</code>文件生成配置bean，然而你为了省事，想继续复用<code>bean.xml</code>，<code>@ImportResource</code>粉墨登场。</p>
<p><strong>bean.xml：</strong></p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?xml version=<span class="string">&quot;1.0&quot;</span> encoding=<span class="string">&quot;UTF-8&quot;</span>?&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">beans</span> <span class="attr">...</span>&quot;&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;haha&quot;</span> <span class="attr">class</span>=<span class="string">&quot;com.lun.boot.bean.User&quot;</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;name&quot;</span> <span class="attr">value</span>=<span class="string">&quot;zhangsan&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">property</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;age&quot;</span> <span class="attr">value</span>=<span class="string">&quot;18&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">property</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">bean</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;hehe&quot;</span> <span class="attr">class</span>=<span class="string">&quot;com.lun.boot.bean.Pet&quot;</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;name&quot;</span> <span class="attr">value</span>=<span class="string">&quot;tomcat&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">property</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">bean</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">beans</span>&gt;</span></span><br></pre></td></tr></table></figure>
<p>使用方法：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@ImportResource(&quot;classpath:beans.xml&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyConfig</span> &#123;</span><br><span class="line">...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>测试类：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">    <span class="comment">//1、返回我们IOC容器</span></span><br><span class="line">    <span class="type">ConfigurableApplicationContext</span> <span class="variable">run</span> <span class="operator">=</span> SpringApplication.run(MainApplication.class, args);</span><br><span class="line"></span><br><span class="line">    <span class="type">boolean</span> <span class="variable">haha</span> <span class="operator">=</span> run.containsBean(<span class="string">&quot;haha&quot;</span>);</span><br><span class="line">    <span class="type">boolean</span> <span class="variable">hehe</span> <span class="operator">=</span> run.containsBean(<span class="string">&quot;hehe&quot;</span>);</span><br><span class="line">    System.out.println(<span class="string">&quot;haha：&quot;</span>+haha);<span class="comment">//true</span></span><br><span class="line">    System.out.println(<span class="string">&quot;hehe：&quot;</span>+hehe);<span class="comment">//true</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="ConfigurationProperties配置绑定"><a href="#ConfigurationProperties配置绑定" class="headerlink" title="@ConfigurationProperties配置绑定"></a>@ConfigurationProperties配置绑定</h2><p>如何使用Java读取到properties文件中的内容，并且把它封装到JavaBean中，以供随时使用</p>
<p><strong>传统方法：</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">getProperties</span> &#123;</span><br><span class="line">     <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> FileNotFoundException, IOException &#123;</span><br><span class="line">         <span class="type">Properties</span> <span class="variable">pps</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Properties</span>();</span><br><span class="line">         pps.load(<span class="keyword">new</span> <span class="title class_">FileInputStream</span>(<span class="string">&quot;a.properties&quot;</span>));</span><br><span class="line">         <span class="type">Enumeration</span> <span class="variable">enum1</span> <span class="operator">=</span> pps.propertyNames();<span class="comment">//得到配置文件的名字</span></span><br><span class="line">         <span class="keyword">while</span>(enum1.hasMoreElements()) &#123;</span><br><span class="line">             <span class="type">String</span> <span class="variable">strKey</span> <span class="operator">=</span> (String) enum1.nextElement();</span><br><span class="line">             <span class="type">String</span> <span class="variable">strValue</span> <span class="operator">=</span> pps.getProperty(strKey);</span><br><span class="line">             System.out.println(strKey + <span class="string">&quot;=&quot;</span> + strValue);</span><br><span class="line">             <span class="comment">//封装到JavaBean。</span></span><br><span class="line">         &#125;</span><br><span class="line">     &#125;</span><br><span class="line"> &#125;</span><br></pre></td></tr></table></figure>
<p>Spring Boot一种<strong>配置绑定</strong>：</p>
<p><code>@ConfigurationProperties</code> + <code>@Component</code></p>
<p>假设有配置文件application.properties</p>
<figure class="highlight properties"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">mycar.brand</span>=<span class="string">BYD</span></span><br><span class="line"><span class="attr">mycar.price</span>=<span class="string">100000</span></span><br></pre></td></tr></table></figure>
<p>只有在容器中的组件，才会拥有SpringBoot提供的强大功能</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="meta">@ConfigurationProperties(prefix = &quot;mycar&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Car</span> &#123;</span><br><span class="line">...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<hr>
<p>Spring Boot另一种<strong>配置绑定</strong>：</p>
<p><code>@EnableConfigurationProperties</code> + <code>@ConfigurationProperties</code></p>
<ol>
<li>开启Car配置绑定功能</li>
<li>把这个Car这个组件自动注册到容器中</li>
</ol>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@EnableConfigurationProperties(Car.class)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyConfig</span> &#123;</span><br><span class="line">...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@ConfigurationProperties(prefix = &quot;mycar&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Car</span> &#123;</span><br><span class="line">...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h1 id="自动配置包规则原理"><a href="#自动配置包规则原理" class="headerlink" title="自动配置包规则原理"></a>自动配置包规则原理</h1><p>Spring Boot应用的启动类：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@SpringBootApplication</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MainApplication</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        SpringApplication.run(MainApplication.class, args);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>分析下<code>@SpringBootApplication</code></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Target(ElementType.TYPE)</span></span><br><span class="line"><span class="meta">@Retention(RetentionPolicy.RUNTIME)</span></span><br><span class="line"><span class="meta">@Documented</span></span><br><span class="line"><span class="meta">@Inherited</span></span><br><span class="line"><span class="meta">@SpringBootConfiguration</span></span><br><span class="line"><span class="meta">@EnableAutoConfiguration</span></span><br><span class="line"><span class="meta">@ComponentScan(</span></span><br><span class="line"><span class="meta">    excludeFilters = &#123;@Filter(</span></span><br><span class="line"><span class="meta">    type = FilterType.CUSTOM,</span></span><br><span class="line"><span class="meta">    classes = &#123;TypeExcludeFilter.class&#125;</span></span><br><span class="line"><span class="meta">), @Filter(</span></span><br><span class="line"><span class="meta">    type = FilterType.CUSTOM,</span></span><br><span class="line"><span class="meta">    classes = &#123;AutoConfigurationExcludeFilter.class&#125;</span></span><br><span class="line"><span class="meta">)&#125;</span></span><br><span class="line"><span class="meta">)</span></span><br><span class="line"><span class="keyword">public</span> <span class="meta">@interface</span> SpringBootApplication &#123;</span><br><span class="line">    ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>重点分析<code>@SpringBootConfiguration</code>，<code>@EnableAutoConfiguration</code>，<code>@ComponentScan</code>。</p>
<h2 id="SpringBootConfiguration"><a href="#SpringBootConfiguration" class="headerlink" title="@SpringBootConfiguration"></a>@SpringBootConfiguration</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Target(ElementType.TYPE)</span></span><br><span class="line"><span class="meta">@Retention(RetentionPolicy.RUNTIME)</span></span><br><span class="line"><span class="meta">@Documented</span></span><br><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="keyword">public</span> <span class="meta">@interface</span> SpringBootConfiguration &#123;</span><br><span class="line">    <span class="meta">@AliasFor(</span></span><br><span class="line"><span class="meta">        annotation = Configuration.class</span></span><br><span class="line"><span class="meta">    )</span></span><br><span class="line">    <span class="type">boolean</span> <span class="title function_">proxyBeanMethods</span><span class="params">()</span> <span class="keyword">default</span> <span class="literal">true</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p><code>@Configuration</code>代表当前是一个配置类。</p>
<h2 id="ComponentScan"><a href="#ComponentScan" class="headerlink" title="@ComponentScan"></a>@ComponentScan</h2><p>指定扫描哪些Spring注解。可见上面的示例</p>
<h2 id="EnableAutoConfiguration"><a href="#EnableAutoConfiguration" class="headerlink" title="@EnableAutoConfiguration"></a>@EnableAutoConfiguration</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Target(ElementType.TYPE)</span></span><br><span class="line"><span class="meta">@Retention(RetentionPolicy.RUNTIME)</span></span><br><span class="line"><span class="meta">@Documented</span></span><br><span class="line"><span class="meta">@Inherited</span></span><br><span class="line"><span class="meta">@AutoConfigurationPackage</span></span><br><span class="line"><span class="meta">@Import(AutoConfigurationImportSelector.class)</span></span><br><span class="line"><span class="keyword">public</span> <span class="meta">@interface</span> EnableAutoConfiguration &#123;</span><br><span class="line">    <span class="type">String</span> <span class="variable">ENABLED_OVERRIDE_PROPERTY</span> <span class="operator">=</span> <span class="string">&quot;spring.boot.enableautoconfiguration&quot;</span>;</span><br><span class="line"></span><br><span class="line">    Class&lt;?&gt;[] exclude() <span class="keyword">default</span> &#123;&#125;;</span><br><span class="line"></span><br><span class="line">    String[] excludeName() <span class="keyword">default</span> &#123;&#125;;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>重点分析<code>@AutoConfigurationPackage</code>，<code>@Import(AutoConfigurationImportSelector.class)</code>。</p>
<h3 id="AutoConfigurationPackage"><a href="#AutoConfigurationPackage" class="headerlink" title="@AutoConfigurationPackage"></a>@AutoConfigurationPackage</h3><p>标签名直译为：自动配置包，指定了默认的包规则。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Target(ElementType.TYPE)</span></span><br><span class="line"><span class="meta">@Retention(RetentionPolicy.RUNTIME)</span></span><br><span class="line"><span class="meta">@Documented</span></span><br><span class="line"><span class="meta">@Inherited</span></span><br><span class="line"><span class="meta">@Import(AutoConfigurationPackages.Registrar.class)</span><span class="comment">//给容器中导入一个组件</span></span><br><span class="line"><span class="keyword">public</span> <span class="meta">@interface</span> AutoConfigurationPackage &#123;</span><br><span class="line">    String[] basePackages() <span class="keyword">default</span> &#123;&#125;;</span><br><span class="line"></span><br><span class="line">    Class&lt;?&gt;[] basePackageClasses() <span class="keyword">default</span> &#123;&#125;;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<ol>
<li>利用Registrar给容器中导入一系列组件</li>
<li>将指定的一个包下的所有组件导入进MainApplication所在包下。</li>
</ol>
<h2 id="初始加载自动配置类"><a href="#初始加载自动配置类" class="headerlink" title="初始加载自动配置类"></a>初始加载自动配置类</h2><h3 id="Import-AutoConfigurationImportSelector-class"><a href="#Import-AutoConfigurationImportSelector-class" class="headerlink" title="@Import(AutoConfigurationImportSelector.class)"></a>@Import(AutoConfigurationImportSelector.class)</h3><ol>
<li>利用<code>getAutoConfigurationEntry(annotationMetadata);</code>给容器中批量导入一些组件</li>
<li>调用<code>List&lt;String&gt; configurations = getCandidateConfigurations(annotationMetadata, attributes)</code>获取到所有需要导入到容器中的配置类</li>
<li>利用工厂加载 <code>Map&lt;String, List&lt;String&gt;&gt; loadSpringFactories(@Nullable ClassLoader classLoader);</code>得到所有的组件</li>
<li>从<code>META-INF/spring.factories</code>位置来加载一个文件。<ul>
<li>默认扫描我们当前系统里面所有<code>META-INF/spring.factories</code>位置的文件</li>
<li><code>spring-boot-autoconfigure-2.3.4.RELEASE.jar</code>包里面也有<code>META-INF/spring.factories</code></li>
</ul>
</li>
</ol>
<center>
    <img style="border-radius: 0.3125em;
    box-shadow: 0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.08);" 
    src="https://gcore.jsdelivr.net/gh/shimmerjordan/pic_bed@main/blog/SpringBoot2/image/20210205005536620.png" width="100%">
    <br>
    <div style="color:orange; border-bottom: 1px solid #d9d9d9;
    display: inline-block;
    color: #999;
    padding: 2px;">加载自动配置类</div>
</center>

<figure class="highlight properties"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 文件里面写死了spring-boot一启动就要给容器中加载的所有配置类</span></span><br><span class="line"><span class="comment"># spring-boot-autoconfigure-2.3.4.RELEASE.jar/META-INF/spring.factories</span></span><br><span class="line"><span class="comment"># Auto Configure</span></span><br><span class="line"><span class="attr">org.springframework.boot.autoconfigure.EnableAutoConfiguration</span>=<span class="string">\</span></span><br><span class="line"><span class="string">org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\</span></span><br><span class="line"><span class="string">org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\</span></span><br><span class="line"><span class="string">...</span></span><br></pre></td></tr></table></figure>
<p>虽然我们127个场景的所有自动配置启动的时候默认全部加载，但是<code>xxxxAutoConfiguration</code>按照条件装配规则（<code>@Conditional</code>），最终会按需配置。</p>
<p>如<code>AopAutoConfiguration</code>类：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Configuration(</span></span><br><span class="line"><span class="meta">    proxyBeanMethods = false</span></span><br><span class="line"><span class="meta">)</span></span><br><span class="line"><span class="meta">@ConditionalOnProperty(</span></span><br><span class="line"><span class="meta">    prefix = &quot;spring.aop&quot;,</span></span><br><span class="line"><span class="meta">    name = &quot;auto&quot;,</span></span><br><span class="line"><span class="meta">    havingValue = &quot;true&quot;,</span></span><br><span class="line"><span class="meta">    matchIfMissing = true</span></span><br><span class="line"><span class="meta">)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">AopAutoConfiguration</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">AopAutoConfiguration</span><span class="params">()</span> &#123;</span><br><span class="line">    &#125;</span><br><span class="line">    ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="自动配置流程"><a href="#自动配置流程" class="headerlink" title="自动配置流程"></a>自动配置流程</h2><p>以<code>DispatcherServletAutoConfiguration</code>的内部类<code>DispatcherServletConfiguration</code>为例子:</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Bean</span></span><br><span class="line"><span class="meta">@ConditionalOnBean(MultipartResolver.class)</span>  <span class="comment">//容器中有这个类型组件</span></span><br><span class="line"><span class="meta">@ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)</span> <span class="comment">//容器中没有这个名字 multipartResolver 的组件</span></span><br><span class="line"><span class="keyword">public</span> MultipartResolver <span class="title function_">multipartResolver</span><span class="params">(MultipartResolver resolver)</span> &#123;</span><br><span class="line">    <span class="comment">//给@Bean标注的方法传入了对象参数，这个参数的值就会从容器中找。</span></span><br><span class="line">    <span class="comment">//SpringMVC multipartResolver。防止有些用户配置的文件上传解析器不符合规范</span></span><br><span class="line">    <span class="comment">// Detect if the user has created a MultipartResolver but named it incorrectly</span></span><br><span class="line">    <span class="keyword">return</span> resolver;<span class="comment">//给容器中加入了文件上传解析器；</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>SpringBoot默认会在底层配好所有的组件，但是<strong>如果用户自己配置了以用户的优先</strong>。</p>
<h2 id="总结："><a href="#总结：" class="headerlink" title="总结："></a>总结：</h2><ul>
<li>SpringBoot先加载所有的自动配置类  <code>xxxxxAutoConfiguration</code></li>
<li>每个自动配置类按照条件进行生效，默认都会绑定配置文件指定的值。（<code>xxxxProperties</code>里面读取，<code>xxxProperties</code>和配置文件进行了绑定）</li>
<li>生效的配置类就会给容器中装配很多组件</li>
<li>只要容器中有这些组件，相当于这些功能就有了</li>
<li>定制化配置<ul>
<li>用户直接自己<code>@Bean</code>替换底层的组件</li>
<li>用户去看这个组件是获取的配置文件什么值就去修改。</li>
</ul>
</li>
</ul>
<p><strong>xxxxxAutoConfiguration —-&gt; 组件 —-&gt; xxxxProperties里面拿值  ——&gt; application.properties</strong></p>
<h1 id="SpringBoot应用编写"><a href="#SpringBoot应用编写" class="headerlink" title="SpringBoot应用编写"></a>SpringBoot应用编写</h1><ul>
<li>引入场景依赖<ul>
<li><a target="_blank" rel="noopener" href="https://docs.spring.io/spring-boot/docs/current/reference/html/using-spring-boot.html#using-boot-starter">官方文档</a></li>
</ul>
</li>
<li>查看自动配置了哪些（选做）<ul>
<li>自己分析，引入场景对应的自动配置一般都生效了</li>
<li>配置文件中debug=true开启自动配置报告。<ul>
<li>Negative（不生效）</li>
<li>Positive（生效）</li>
</ul>
</li>
</ul>
</li>
<li>是否需要修改<ul>
<li>参照文档修改配置项<ul>
<li><a target="_blank" rel="noopener" href="https://docs.spring.io/spring-boot/docs/current/reference/html/appendix-application-properties.html#common-application-properties">官方文档</a></li>
<li>自己分析。xxxxProperties绑定了配置文件的哪些。</li>
</ul>
</li>
<li>自定义加入或者替换组件<ul>
<li>@Bean、@Component…</li>
</ul>
</li>
<li>自定义器  XXXXXCustomizer；</li>
<li>……</li>
</ul>
</li>
</ul>
<h2 id="Lombok简化开发"><a href="#Lombok简化开发" class="headerlink" title="Lombok简化开发"></a>Lombok简化开发</h2><p>Lombok用标签方式代替构造器、getter/setter、toString()等鸡肋代码。</p>
<p>spring boot已经管理Lombok。引入依赖：</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"> <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">     <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.projectlombok<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">     <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>lombok<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure>
<p>IDEA中File-&gt;Settings-&gt;Plugins，搜索安装Lombok插件。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@NoArgsConstructor</span></span><br><span class="line"><span class="comment">//@AllArgsConstructor</span></span><br><span class="line"><span class="meta">@Data</span></span><br><span class="line"><span class="meta">@ToString</span></span><br><span class="line"><span class="meta">@EqualsAndHashCode</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">User</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> String name;</span><br><span class="line">    <span class="keyword">private</span> Integer age;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> Pet pet;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">User</span><span class="params">(String name,Integer age)</span>&#123;</span><br><span class="line">        <span class="built_in">this</span>.name = name;</span><br><span class="line">        <span class="built_in">this</span>.age = age;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<hr>
<p>简化日志开发</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Slf4j</span></span><br><span class="line"><span class="meta">@RestController</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">HelloController</span> &#123;</span><br><span class="line">    <span class="meta">@RequestMapping(&quot;/hello&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">handle01</span><span class="params">(<span class="meta">@RequestParam(&quot;name&quot;)</span> String name)</span>&#123;</span><br><span class="line">        log.info(<span class="string">&quot;请求进来了....&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;Hello, Spring Boot 2!&quot;</span>+<span class="string">&quot;你好：&quot;</span>+name;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="dev-tools"><a href="#dev-tools" class="headerlink" title="dev-tools"></a>dev-tools</h2><blockquote>
<p>Spring Boot includes an additional set of tools that can make the application development experience a little more pleasant. The <code>spring-boot-devtools</code> module can be included in any project to provide additional development-time features.——<a target="_blank" rel="noopener" href="https://docs.spring.io/spring-boot/docs/2.3.8.RELEASE/reference/html/using-spring-boot.html#using-boot-devtools">link</a></p>
<p>Applications that use <code>spring-boot-devtools</code> automatically restart whenever files on the classpath change. This can be a useful feature when working in an IDE, as it gives a very fast feedback loop for code changes. By default, any entry on the classpath that points to a directory is monitored for changes. Note that certain resources, such as static assets and view templates, <a target="_blank" rel="noopener" href="https://docs.spring.io/spring-boot/docs/2.3.8.RELEASE/reference/html/using-spring-boot.html#using-boot-devtools-restart-exclude">do not need to restart the application</a>.——<a target="_blank" rel="noopener" href="https://docs.spring.io/spring-boot/docs/2.3.8.RELEASE/reference/html/using-spring-boot.html#using-boot-devtools-restart">link</a></p>
<p>Triggering a restart</p>
<p>As DevTools monitors classpath resources, the only way to trigger a restart is to update the classpath. The way in which you cause the classpath to be updated depends on the IDE that you are using:</p>
<ul>
<li>In Eclipse, saving a modified file causes the classpath to be updated and triggers a restart.</li>
<li>In IntelliJ IDEA, building the project (<code>Build -&gt; Build Project</code>)(shortcut: Ctrl+F9) has the same effect.</li>
</ul>
</blockquote>
<p>添加依赖：</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependencies</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-devtools<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">optional</span>&gt;</span>true<span class="tag">&lt;/<span class="name">optional</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependencies</span>&gt;</span></span><br></pre></td></tr></table></figure>
<p>在IDEA中，项目或者页面修改以后：Ctrl+F9。</p>
<h2 id="Spring-Initailizr"><a href="#Spring-Initailizr" class="headerlink" title="Spring Initailizr"></a>Spring Initailizr</h2><p><a target="_blank" rel="noopener" href="https://start.spring.io/">Spring Initailizr</a>是创建Spring Boot工程向导。</p>
<p>在IDEA中，菜单栏New -&gt; Project -&gt; Spring Initailizr。</p>
<h2 id="yaml的用法"><a href="#yaml的用法" class="headerlink" title="yaml的用法"></a>yaml的用法</h2><p>同以前的properties用法</p>
<p>YAML 是 “YAML Ain’t Markup Language”（YAML 不是一种标记语言）的递归缩写。在开发的这种语言时，YAML 的意思其实是：”Yet Another Markup Language”（仍是一种标记语言）。 </p>
<p><strong>非常适合用来做以数据为中心的配置文件</strong>。</p>
<h3 id="基本语法"><a href="#基本语法" class="headerlink" title="基本语法"></a>基本语法</h3><ul>
<li>key: value；kv之间有空格</li>
<li>大小写敏感</li>
<li>使用缩进表示层级关系</li>
<li>缩进不允许使用tab，只允许空格</li>
<li>缩进的空格数不重要，只要相同层级的元素左对齐即可</li>
<li>‘#’表示注释</li>
<li>字符串无需加引号，如果要加，单引号’’、双引号””表示字符串内容会被 转义、不转义</li>
</ul>
<h3 id="数据类型"><a href="#数据类型" class="headerlink" title="数据类型"></a>数据类型</h3><ul>
<li>字面量：单个的、不可再分的值。date、boolean、string、number、null</li>
</ul>
<figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">k:</span> <span class="string">v</span></span><br></pre></td></tr></table></figure>
<ul>
<li>对象：键值对的集合。map、hash、set、object </li>
</ul>
<figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#行内写法：  </span></span><br><span class="line"></span><br><span class="line"><span class="attr">k:</span> &#123;<span class="string">k1:v1</span>,<span class="string">k2:v2</span>,<span class="string">k3:v3</span>&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">#或</span></span><br><span class="line"></span><br><span class="line"><span class="attr">k:</span> </span><br><span class="line">  <span class="attr">k1:</span> <span class="string">v1</span></span><br><span class="line">  <span class="attr">k2:</span> <span class="string">v2</span></span><br><span class="line">  <span class="attr">k3:</span> <span class="string">v3</span></span><br></pre></td></tr></table></figure>
<ul>
<li>数组：一组按次序排列的值。array、list、queue</li>
</ul>
<figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#行内写法：  </span></span><br><span class="line"></span><br><span class="line"><span class="attr">k:</span> [<span class="string">v1</span>,<span class="string">v2</span>,<span class="string">v3</span>]</span><br><span class="line"></span><br><span class="line"><span class="comment">#或者</span></span><br><span class="line"></span><br><span class="line"><span class="attr">k:</span></span><br><span class="line"> <span class="bullet">-</span> <span class="string">v1</span></span><br><span class="line"> <span class="bullet">-</span> <span class="string">v2</span></span><br><span class="line"> <span class="bullet">-</span> <span class="string">v3</span></span><br></pre></td></tr></table></figure>
<h3 id="实例"><a href="#实例" class="headerlink" title="实例"></a>实例</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Data</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Person</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> String userName;</span><br><span class="line">    <span class="keyword">private</span> Boolean boss;</span><br><span class="line">    <span class="keyword">private</span> Date birth;</span><br><span class="line">    <span class="keyword">private</span> Integer age;</span><br><span class="line">    <span class="keyword">private</span> Pet pet;</span><br><span class="line">    <span class="keyword">private</span> String[] interests;</span><br><span class="line">    <span class="keyword">private</span> List&lt;String&gt; animal;</span><br><span class="line">    <span class="keyword">private</span> Map&lt;String, Object&gt; score;</span><br><span class="line">    <span class="keyword">private</span> Set&lt;Double&gt; salarys;</span><br><span class="line">    <span class="keyword">private</span> Map&lt;String, List&lt;Pet&gt;&gt; allPets;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Data</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Pet</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> String name;</span><br><span class="line">    <span class="keyword">private</span> Double weight;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>用yaml表示以上对象</p>
<figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">person:</span></span><br><span class="line">  <span class="attr">userName:</span> <span class="string">zhangsan</span></span><br><span class="line">  <span class="attr">boss:</span> <span class="literal">false</span></span><br><span class="line">  <span class="attr">birth:</span> <span class="number">2019</span><span class="string">/12/12</span> <span class="number">20</span><span class="string">:12:33</span></span><br><span class="line">  <span class="attr">age:</span> <span class="number">18</span></span><br><span class="line">  <span class="attr">pet:</span> </span><br><span class="line">    <span class="attr">name:</span> <span class="string">tomcat</span></span><br><span class="line">    <span class="attr">weight:</span> <span class="number">23.4</span></span><br><span class="line">  <span class="attr">interests:</span> [<span class="string">篮球</span>,<span class="string">游泳</span>]</span><br><span class="line">  <span class="attr">animal:</span> </span><br><span class="line">    <span class="bullet">-</span> <span class="string">jerry</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">mario</span></span><br><span class="line">  <span class="attr">score:</span></span><br><span class="line">    <span class="attr">english:</span> </span><br><span class="line">      <span class="attr">first:</span> <span class="number">30</span></span><br><span class="line">      <span class="attr">second:</span> <span class="number">40</span></span><br><span class="line">      <span class="attr">third:</span> <span class="number">50</span></span><br><span class="line">    <span class="attr">math:</span> [<span class="number">131</span>,<span class="number">140</span>,<span class="number">148</span>]</span><br><span class="line">    <span class="attr">chinese:</span> &#123;<span class="attr">first:</span> <span class="number">128</span>,<span class="attr">second:</span> <span class="number">136</span>&#125;</span><br><span class="line">  <span class="attr">salarys:</span> [<span class="number">3999</span>,<span class="number">4999.98</span>,<span class="number">5999.99</span>]</span><br><span class="line">  <span class="attr">allPets:</span></span><br><span class="line">    <span class="attr">sick:</span></span><br><span class="line">      <span class="bullet">-</span> &#123;<span class="attr">name:</span> <span class="string">tom</span>&#125;</span><br><span class="line">      <span class="bullet">-</span> &#123;<span class="attr">name:</span> <span class="string">jerry</span>,<span class="attr">weight:</span> <span class="number">47</span>&#125;</span><br><span class="line">    <span class="attr">health:</span> [&#123;<span class="attr">name:</span> <span class="string">mario</span>,<span class="attr">weight:</span> <span class="number">47</span>&#125;]</span><br></pre></td></tr></table></figure>
<h2 id="自定义类绑定的配置提示"><a href="#自定义类绑定的配置提示" class="headerlink" title="自定义类绑定的配置提示"></a>自定义类绑定的配置提示</h2><blockquote>
<p>You can easily generate your own configuration metadata file from items annotated with <code>@ConfigurationProperties</code> by using the <code>spring-boot-configuration-processor</code> jar. The jar includes a Java annotation processor which is invoked as your project is compiled.——<a target="_blank" rel="noopener" href="https://docs.spring.io/spring-boot/docs/2.4.2/reference/htmlsingle/#configuration-metadata-annotation-processor">link</a></p>
</blockquote>
<p>自定义的类和配置文件绑定一般没有提示。若要提示，添加如下依赖：</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-configuration-processor<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">optional</span>&gt;</span>true<span class="tag">&lt;/<span class="name">optional</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- 下面插件作用是工程打包时，不将spring-boot-configuration-processor打进包内，让其只在编码的时候有用 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">build</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">plugins</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">plugin</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-maven-plugin<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">configuration</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">excludes</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;<span class="name">exclude</span>&gt;</span></span><br><span class="line">                        <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">                        <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-configuration-processor<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;/<span class="name">exclude</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;/<span class="name">excludes</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;/<span class="name">configuration</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">plugin</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">plugins</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">build</span>&gt;</span></span><br></pre></td></tr></table></figure>
<h1 id="web开发简介"><a href="#web开发简介" class="headerlink" title="web开发简介"></a>web开发简介</h1><p>Spring Boot provides auto-configuration for Spring MVC that <strong>works well with most applications.(大多场景我们都无需自定义配置)</strong></p>
<p>The auto-configuration adds the following features on top of Spring’s defaults:</p>
<ul>
<li><p>Inclusion of <code>ContentNegotiatingViewResolver</code> and <code>BeanNameViewResolver</code> beans.</p>
<ul>
<li>内容协商视图解析器和BeanName视图解析器</li>
</ul>
</li>
<li><p>Support for serving static resources, including support for WebJars (covered <a target="_blank" rel="noopener" href="https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-spring-mvc-static-content">later in this document</a>)).</p>
<ul>
<li>静态资源（包括webjars）</li>
</ul>
</li>
<li><p>Automatic registration of <code>Converter</code>, <code>GenericConverter</code>, and <code>Formatter</code> beans.</p>
<ul>
<li>自动注册 <code>Converter，GenericConverter，Formatter</code></li>
</ul>
</li>
<li><p>Support for <code>HttpMessageConverters</code> (covered <a target="_blank" rel="noopener" href="https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-spring-mvc-message-converters">later in this document</a>).</p>
<ul>
<li>支持 <code>HttpMessageConverters</code> （后来我们配合内容协商理解原理）</li>
</ul>
</li>
<li><p>Automatic registration of <code>MessageCodesResolver</code> (covered <a target="_blank" rel="noopener" href="https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-spring-message-codes">later in this document</a>).</p>
<ul>
<li>自动注册 <code>MessageCodesResolver</code> （国际化用）</li>
</ul>
</li>
<li><p>Static <code>index.html</code> support.</p>
<ul>
<li>静态index.html 页支持</li>
</ul>
</li>
<li><p>Custom <code>Favicon</code> support (covered <a target="_blank" rel="noopener" href="https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-spring-mvc-favicon">later in this document</a>).</p>
<ul>
<li>自定义 <code>Favicon</code>  </li>
</ul>
</li>
<li><p>Automatic use of a <code>ConfigurableWebBindingInitializer</code> bean (covered <a target="_blank" rel="noopener" href="https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-spring-mvc-web-binding-initializer">later in this document</a>).</p>
<ul>
<li>自动使用 <code>ConfigurableWebBindingInitializer</code> ，（DataBinder负责将请求数据绑定到JavaBean上）</li>
</ul>
</li>
</ul>
<blockquote>
<p>If you want to keep those Spring Boot MVC customizations and make more <a target="_blank" rel="noopener" href="https://docs.spring.io/spring/docs/5.2.9.RELEASE/spring-framework-reference/web.html#mvc">MVC customizations</a> (interceptors, formatters, view controllers, and other features), you can add your own <code>@Configuration</code> class of type <code>WebMvcConfigurer</code> but <strong>without</strong> <code>@EnableWebMvc</code>.</p>
<p><strong>不用@EnableWebMvc注解。使用</strong> <strong><code>@Configuration</code></strong> <strong>+</strong> <strong><code>WebMvcConfigurer</code></strong> <strong>自定义规则</strong></p>
<p>If you want to provide custom instances of <code>RequestMappingHandlerMapping</code>, <code>RequestMappingHandlerAdapter</code>, or <code>ExceptionHandlerExceptionResolver</code>, and still keep the Spring Boot MVC customizations, you can declare a bean of type <code>WebMvcRegistrations</code> and use it to provide custom instances of those components.</p>
<p><strong>声明</strong> <strong><code>WebMvcRegistrations</code></strong> <strong>改变默认底层组件</strong></p>
<p>If you want to take complete control of Spring MVC, you can add your own <code>@Configuration</code> annotated with <code>@EnableWebMvc</code>, or alternatively add your own <code>@Configuration</code>-annotated <code>DelegatingWebMvcConfiguration</code> as described in the Javadoc of <code>@EnableWebMvc</code>.</p>
<p><strong>使用</strong> <strong><code>@EnableWebMvc+@Configuration+DelegatingWebMvcConfiguration 全面接管SpringMVC</code></strong></p>
</blockquote>
<h2 id="静态资源规则与定制化"><a href="#静态资源规则与定制化" class="headerlink" title="静态资源规则与定制化"></a>静态资源规则与定制化</h2><h3 id="静态资源目录"><a href="#静态资源目录" class="headerlink" title="静态资源目录"></a>静态资源目录</h3><p>只要静态资源放在类路径下： called <code>/static</code> (or <code>/public</code> or <code>/resources</code> or <code>/META-INF/resources</code></p>
<p>访问 ： 当前项目根路径/ + 静态资源名 </p>
<p>原理： 静态映射/**。</p>
<p>请求进来，先去找Controller看能不能处理。不能处理的所有请求又都交给静态资源处理器。静态资源也找不到则响应404页面。</p>
<p>也可以改变默认的静态资源路径，<code>/static</code>，<code>/public</code>,<code>/resources</code>, <code>/META-INF/resources</code>失效</p>
<figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">resources:</span></span><br><span class="line">  <span class="attr">static-locations:</span> [<span class="string">classpath:/haha/</span>]</span><br></pre></td></tr></table></figure>
<h3 id="静态资源访问前缀"><a href="#静态资源访问前缀" class="headerlink" title="静态资源访问前缀"></a>静态资源访问前缀</h3><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="attr">mvc:</span></span><br><span class="line">    <span class="attr">static-path-pattern:</span> <span class="string">/res/**</span></span><br></pre></td></tr></table></figure>
<p>当前项目 + static-path-pattern + 静态资源名 = 静态资源文件夹下找</p>
<h3 id="webjar"><a href="#webjar" class="headerlink" title="webjar"></a>webjar</h3><p>可用jar方式添加css，js等资源文件，<a target="_blank" rel="noopener" href="https://www.webjars.org/">https://www.webjars.org/</a>。例如，添加jquery</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.webjars<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>jquery<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>3.5.1<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure>
<p>访问地址：<a target="_blank" rel="noopener" href="http://localhost:8080/webjars/jquery/3.5.1/jquery.js">http://localhost:8080/webjars/<strong>jquery/3.5.1/jquery.js</strong></a>  后面地址要按照依赖里面的包路径。</p>
<h2 id="welcome与favicon功能"><a href="#welcome与favicon功能" class="headerlink" title="welcome与favicon功能"></a>welcome与favicon功能</h2><p><a target="_blank" rel="noopener" href="https://docs.spring.io/spring-boot/docs/2.3.8.RELEASE/reference/htmlsingle/#boot-features-spring-mvc-welcome-page">官方文档</a></p>
<h3 id="欢迎页支持"><a href="#欢迎页支持" class="headerlink" title="欢迎页支持"></a>欢迎页支持</h3><ul>
<li><p>静态资源路径下 index.html。</p>
<ul>
<li>可以配置静态资源路径</li>
<li>但是不可以配置静态资源的访问前缀。否则导致 index.html不能被默认访问</li>
</ul>
</li>
</ul>
<figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">spring:</span></span><br><span class="line"><span class="comment">#  mvc:</span></span><br><span class="line"><span class="comment">#    static-path-pattern: /res/**   这个会导致welcome page功能失效</span></span><br><span class="line">  <span class="attr">resources:</span></span><br><span class="line">    <span class="attr">static-locations:</span> [<span class="string">classpath:/haha/</span>]</span><br></pre></td></tr></table></figure>
<ul>
<li>controller能处理/index。</li>
</ul>
<h3 id="自定义Favicon"><a href="#自定义Favicon" class="headerlink" title="自定义Favicon"></a>自定义Favicon</h3><p>指网页标签上的小图标。</p>
<p>favicon.ico 放在静态资源目录下即可。</p>
<figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">spring:</span></span><br><span class="line"><span class="comment">#  mvc:</span></span><br><span class="line"><span class="comment">#    static-path-pattern: /res/**   这个会导致 Favicon 功能失效</span></span><br></pre></td></tr></table></figure>
<h2 id="静态资源原理"><a href="#静态资源原理" class="headerlink" title="静态资源原理"></a>静态资源原理</h2><ul>
<li>SpringBoot启动默认加载  <code>xxxAutoConfiguration</code> 类（自动配置类）</li>
<li>SpringMVC功能的自动配置类<code>WebMvcAutoConfiguration</code>，生效</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Configuration(proxyBeanMethods = false)</span></span><br><span class="line"><span class="meta">@ConditionalOnWebApplication(type = Type.SERVLET)</span></span><br><span class="line"><span class="meta">@ConditionalOnClass(&#123; Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class &#125;)</span></span><br><span class="line"><span class="meta">@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)</span></span><br><span class="line"><span class="meta">@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)</span></span><br><span class="line"><span class="meta">@AutoConfigureAfter(&#123; DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,</span></span><br><span class="line"><span class="meta">        ValidationAutoConfiguration.class &#125;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">WebMvcAutoConfiguration</span> &#123;</span><br><span class="line">    ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<ul>
<li>给容器中配置的内容：<ul>
<li>配置文件的相关属性的绑定：<code>WebMvcProperties==spring.mvc</code>、<code>ResourceProperties==spring.resources</code></li>
</ul>
</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Configuration(proxyBeanMethods = false)</span></span><br><span class="line"><span class="meta">@Import(EnableWebMvcConfiguration.class)</span></span><br><span class="line"><span class="meta">@EnableConfigurationProperties(&#123; WebMvcProperties.class, ResourceProperties.class &#125;)</span></span><br><span class="line"><span class="meta">@Order(0)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">WebMvcAutoConfigurationAdapter</span> <span class="keyword">implements</span> <span class="title class_">WebMvcConfigurer</span> &#123;</span><br><span class="line">    ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h3 id="配置类只有一个有参构造器"><a href="#配置类只有一个有参构造器" class="headerlink" title="配置类只有一个有参构造器"></a>配置类只有一个有参构造器</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">////有参构造器所有参数的值都会从容器中确定</span></span><br><span class="line"><span class="keyword">public</span> <span class="title function_">WebMvcAutoConfigurationAdapter</span><span class="params">(WebProperties webProperties, WebMvcProperties mvcProperties,</span></span><br><span class="line"><span class="params">        ListableBeanFactory beanFactory, ObjectProvider&lt;HttpMessageConverters&gt; messageConvertersProvider,</span></span><br><span class="line"><span class="params">        ObjectProvider&lt;ResourceHandlerRegistrationCustomizer&gt; resourceHandlerRegistrationCustomizerProvider,</span></span><br><span class="line"><span class="params">        ObjectProvider&lt;DispatcherServletPath&gt; dispatcherServletPath,</span></span><br><span class="line"><span class="params">        ObjectProvider&lt;ServletRegistrationBean&lt;?&gt;&gt; servletRegistrations)</span> &#123;</span><br><span class="line">    <span class="built_in">this</span>.mvcProperties = mvcProperties;</span><br><span class="line">    <span class="built_in">this</span>.beanFactory = beanFactory;</span><br><span class="line">    <span class="built_in">this</span>.messageConvertersProvider = messageConvertersProvider;</span><br><span class="line">    <span class="built_in">this</span>.resourceHandlerRegistrationCustomizer = resourceHandlerRegistrationCustomizerProvider.getIfAvailable();</span><br><span class="line">    <span class="built_in">this</span>.dispatcherServletPath = dispatcherServletPath;</span><br><span class="line">    <span class="built_in">this</span>.servletRegistrations = servletRegistrations;</span><br><span class="line">    <span class="built_in">this</span>.mvcProperties.checkConfiguration();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<ul>
<li>ResourceProperties resourceProperties；获取和spring.resources绑定的所有的值的对象</li>
<li>WebMvcProperties mvcProperties 获取和spring.mvc绑定的所有的值的对象</li>
<li>ListableBeanFactory beanFactory Spring的beanFactory</li>
<li>HttpMessageConverters 找到所有的HttpMessageConverters</li>
<li>ResourceHandlerRegistrationCustomizer 找到 资源处理器的自定义器。</li>
<li>DispatcherServletPath</li>
<li>ServletRegistrationBean   给应用注册Servlet、Filter….</li>
</ul>
<h3 id="资源处理的默认规则"><a href="#资源处理的默认规则" class="headerlink" title="资源处理的默认规则"></a>资源处理的默认规则</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line">...</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">WebMvcAutoConfiguration</span> &#123;</span><br><span class="line">    ...</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">EnableWebMvcConfiguration</span> <span class="keyword">extends</span> <span class="title class_">DelegatingWebMvcConfiguration</span> <span class="keyword">implements</span> <span class="title class_">ResourceLoaderAware</span> &#123;</span><br><span class="line">        ...</span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">addResourceHandlers</span><span class="params">(ResourceHandlerRegistry registry)</span> &#123;</span><br><span class="line">            <span class="built_in">super</span>.addResourceHandlers(registry);</span><br><span class="line">            <span class="keyword">if</span> (!<span class="built_in">this</span>.resourceProperties.isAddMappings()) &#123;</span><br><span class="line">                logger.debug(<span class="string">&quot;Default resource handling disabled&quot;</span>);</span><br><span class="line">                <span class="keyword">return</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="type">ServletContext</span> <span class="variable">servletContext</span> <span class="operator">=</span> getServletContext();</span><br><span class="line">            addResourceHandler(registry, <span class="string">&quot;/webjars/**&quot;</span>, <span class="string">&quot;classpath:/META-INF/resources/webjars/&quot;</span>);</span><br><span class="line">            addResourceHandler(registry, <span class="built_in">this</span>.mvcProperties.getStaticPathPattern(), (registration) -&gt; &#123;</span><br><span class="line">                registration.addResourceLocations(<span class="built_in">this</span>.resourceProperties.getStaticLocations());</span><br><span class="line">                <span class="keyword">if</span> (servletContext != <span class="literal">null</span>) &#123;</span><br><span class="line">                    registration.addResourceLocations(<span class="keyword">new</span> <span class="title class_">ServletContextResource</span>(servletContext, SERVLET_LOCATION));</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;);</span><br><span class="line">        &#125;</span><br><span class="line">        ...</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">    ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>根据上述代码，我们可以同过配置禁止所有静态资源规则。</p>
<figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="attr">resources:</span></span><br><span class="line">    <span class="attr">add-mappings:</span> <span class="literal">false</span>   <span class="comment">#禁用所有静态资源规则</span></span><br></pre></td></tr></table></figure>
<p>静态资源规则：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@ConfigurationProperties(prefix = &quot;spring.resources&quot;, ignoreUnknownFields = false)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ResourceProperties</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> String[] CLASSPATH_RESOURCE_LOCATIONS = &#123; <span class="string">&quot;classpath:/META-INF/resources/&quot;</span>,</span><br><span class="line">            <span class="string">&quot;classpath:/resources/&quot;</span>, <span class="string">&quot;classpath:/static/&quot;</span>, <span class="string">&quot;classpath:/public/&quot;</span> &#125;;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Locations of static resources. Defaults to classpath:[/META-INF/resources/,</span></span><br><span class="line"><span class="comment">     * /resources/, /static/, /public/].</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> String[] staticLocations = CLASSPATH_RESOURCE_LOCATIONS;</span><br><span class="line">    ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h3 id="欢迎页的处理规则"><a href="#欢迎页的处理规则" class="headerlink" title="欢迎页的处理规则"></a>欢迎页的处理规则</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">...</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">WebMvcAutoConfiguration</span> &#123;</span><br><span class="line">    ...</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">EnableWebMvcConfiguration</span> <span class="keyword">extends</span> <span class="title class_">DelegatingWebMvcConfiguration</span> <span class="keyword">implements</span> <span class="title class_">ResourceLoaderAware</span> &#123;</span><br><span class="line">        ...</span><br><span class="line">        <span class="meta">@Bean</span></span><br><span class="line">        <span class="keyword">public</span> WelcomePageHandlerMapping <span class="title function_">welcomePageHandlerMapping</span><span class="params">(ApplicationContext applicationContext,</span></span><br><span class="line"><span class="params">                FormattingConversionService mvcConversionService, ResourceUrlProvider mvcResourceUrlProvider)</span> &#123;</span><br><span class="line">            <span class="type">WelcomePageHandlerMapping</span> <span class="variable">welcomePageHandlerMapping</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">WelcomePageHandlerMapping</span>(</span><br><span class="line">                    <span class="keyword">new</span> <span class="title class_">TemplateAvailabilityProviders</span>(applicationContext), applicationContext, getWelcomePage(),</span><br><span class="line">                    <span class="built_in">this</span>.mvcProperties.getStaticPathPattern());</span><br><span class="line">            welcomePageHandlerMapping.setInterceptors(getInterceptors(mvcConversionService, mvcResourceUrlProvider));</span><br><span class="line">            welcomePageHandlerMapping.setCorsConfigurations(getCorsConfigurations());</span><br><span class="line">            <span class="keyword">return</span> welcomePageHandlerMapping;</span><br><span class="line">        &#125;</span><br></pre></td></tr></table></figure>
<p><code>WelcomePageHandlerMapping</code>的构造方法如下：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">WelcomePageHandlerMapping(TemplateAvailabilityProviders templateAvailabilityProviders,</span><br><span class="line">                          ApplicationContext applicationContext, Resource welcomePage, String staticPathPattern) &#123;</span><br><span class="line">    <span class="keyword">if</span> (welcomePage != <span class="literal">null</span> &amp;&amp; <span class="string">&quot;/**&quot;</span>.equals(staticPathPattern)) &#123;</span><br><span class="line">        <span class="comment">//要用欢迎页功能，必须是/**</span></span><br><span class="line">        logger.info(<span class="string">&quot;Adding welcome page: &quot;</span> + welcomePage);</span><br><span class="line">        setRootViewName(<span class="string">&quot;forward:index.html&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span> (welcomeTemplateExists(templateAvailabilityProviders, applicationContext)) &#123;</span><br><span class="line">        <span class="comment">//调用Controller /index</span></span><br><span class="line">        logger.info(<span class="string">&quot;Adding welcome page template: index&quot;</span>);</span><br><span class="line">        setRootViewName(<span class="string">&quot;index&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>这构造方法内的代码也解释了<a href="#">web场景-welcome与favicon功能</a>中配置<code>static-path-pattern</code>了，welcome页面和小图标失效的问题。</p>
<h1 id="Rest映射及源码解析"><a href="#Rest映射及源码解析" class="headerlink" title="Rest映射及源码解析"></a>Rest映射及源码解析</h1><h3 id="请求映射"><a href="#请求映射" class="headerlink" title="请求映射"></a>请求映射</h3><ul>
<li><p>@xxxMapping;</p>
<ul>
<li>@GetMapping</li>
<li>@PostMapping</li>
<li>@PutMapping</li>
<li>@DeleteMapping</li>
</ul>
</li>
<li><p>Rest风格支持（使用<strong>HTTP</strong>请求方式动词来表示对资源的操作）</p>
<ul>
<li>以前：<ul>
<li>/getUser 获取用户</li>
<li>/deleteUser 删除用户</li>
<li>/editUser 修改用户</li>
<li>/saveUser保存用户</li>
</ul>
</li>
<li>现在： /user <ul>
<li>GET-获取用户</li>
<li>DELETE-删除用户</li>
<li>PUT-修改用户</li>
<li>POST-保存用户</li>
</ul>
</li>
<li>核心Filter；<strong>HiddenHttpMethodFilter</strong></li>
</ul>
</li>
<li><p><strong>用法</strong></p>
<ul>
<li>开启页面表单的Rest功能</li>
<li>页面 form的属性<code>method=pos</code>t，隐藏域<code>\_method=put</code>、<code>delete</code>等（如果直接get或post，无需隐藏域）</li>
<li>编写请求映射</li>
</ul>
</li>
</ul>
<figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="attr">mvc:</span></span><br><span class="line">    <span class="attr">hiddenmethod:</span></span><br><span class="line">      <span class="attr">filter:</span></span><br><span class="line">        <span class="attr">enabled:</span> <span class="literal">true</span>   <span class="comment">#开启页面表单的Rest功能</span></span><br></pre></td></tr></table></figure>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">form</span> <span class="attr">action</span>=<span class="string">&quot;/user&quot;</span> <span class="attr">method</span>=<span class="string">&quot;get&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">input</span> <span class="attr">value</span>=<span class="string">&quot;REST-GET提交&quot;</span> <span class="attr">type</span>=<span class="string">&quot;submit&quot;</span> /&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">form</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">form</span> <span class="attr">action</span>=<span class="string">&quot;/user&quot;</span> <span class="attr">method</span>=<span class="string">&quot;post&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">input</span> <span class="attr">value</span>=<span class="string">&quot;REST-POST提交&quot;</span> <span class="attr">type</span>=<span class="string">&quot;submit&quot;</span> /&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">form</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">form</span> <span class="attr">action</span>=<span class="string">&quot;/user&quot;</span> <span class="attr">method</span>=<span class="string">&quot;post&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">input</span> <span class="attr">name</span>=<span class="string">&quot;_method&quot;</span> <span class="attr">type</span>=<span class="string">&quot;hidden&quot;</span> <span class="attr">value</span>=<span class="string">&quot;DELETE&quot;</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">input</span> <span class="attr">value</span>=<span class="string">&quot;REST-DELETE 提交&quot;</span> <span class="attr">type</span>=<span class="string">&quot;submit&quot;</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">form</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">form</span> <span class="attr">action</span>=<span class="string">&quot;/user&quot;</span> <span class="attr">method</span>=<span class="string">&quot;post&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">input</span> <span class="attr">name</span>=<span class="string">&quot;_method&quot;</span> <span class="attr">type</span>=<span class="string">&quot;hidden&quot;</span> <span class="attr">value</span>=<span class="string">&quot;PUT&quot;</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">input</span> <span class="attr">value</span>=<span class="string">&quot;REST-PUT提交&quot;</span><span class="attr">type</span>=<span class="string">&quot;submit&quot;</span> /&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">form</span>&gt;</span></span><br></pre></td></tr></table></figure>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@GetMapping(&quot;/user&quot;)</span></span><br><span class="line"><span class="comment">//@RequestMapping(value = &quot;/user&quot;,method = RequestMethod.GET)</span></span><br><span class="line"><span class="keyword">public</span> String <span class="title function_">getUser</span><span class="params">()</span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;GET-张三&quot;</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@PostMapping(&quot;/user&quot;)</span></span><br><span class="line"><span class="comment">//@RequestMapping(value = &quot;/user&quot;,method = RequestMethod.POST)</span></span><br><span class="line"><span class="keyword">public</span> String <span class="title function_">saveUser</span><span class="params">()</span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;POST-张三&quot;</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@PutMapping(&quot;/user&quot;)</span></span><br><span class="line"><span class="comment">//@RequestMapping(value = &quot;/user&quot;,method = RequestMethod.PUT)</span></span><br><span class="line"><span class="keyword">public</span> String <span class="title function_">putUser</span><span class="params">()</span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;PUT-张三&quot;</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@DeleteMapping(&quot;/user&quot;)</span></span><br><span class="line"><span class="comment">//@RequestMapping(value = &quot;/user&quot;,method = RequestMethod.DELETE)</span></span><br><span class="line"><span class="keyword">public</span> String <span class="title function_">deleteUser</span><span class="params">()</span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;DELETE-张三&quot;</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<ul>
<li>Rest原理（表单提交要使用REST的时候）<ul>
<li>表单提交会带上<code>\_method=PUT</code></li>
<li><strong>请求过来被</strong><code>HiddenHttpMethodFilter</code>拦截<ul>
<li>请求是否正常，并且是POST<ul>
<li>获取到<code>\_method</code>的值。</li>
<li>兼容以下请求；<strong>PUT</strong>.<strong>DELETE</strong>.<strong>PATCH</strong></li>
<li><strong>原生request（post），包装模式requesWrapper重写了getMethod方法，返回的是传入的值。</strong></li>
<li><strong>过滤器链放行的时候用wrapper。以后的方法调用getMethod是调用requestWrapper的。</strong></li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">HiddenHttpMethodFilter</span> <span class="keyword">extends</span> <span class="title class_">OncePerRequestFilter</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> List&lt;String&gt; ALLOWED_METHODS =</span><br><span class="line">            Collections.unmodifiableList(Arrays.asList(HttpMethod.PUT.name(),</span><br><span class="line">                    HttpMethod.DELETE.name(), HttpMethod.PATCH.name()));</span><br><span class="line"></span><br><span class="line">    <span class="comment">/** Default method parameter: &#123;<span class="doctag">@code</span> _method&#125;. */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">DEFAULT_METHOD_PARAM</span> <span class="operator">=</span> <span class="string">&quot;_method&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="type">String</span> <span class="variable">methodParam</span> <span class="operator">=</span> DEFAULT_METHOD_PARAM;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Set the parameter name to look for HTTP methods.</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@see</span> #DEFAULT_METHOD_PARAM</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setMethodParam</span><span class="params">(String methodParam)</span> &#123;</span><br><span class="line">        Assert.hasText(methodParam, <span class="string">&quot;&#x27;methodParam&#x27; must not be empty&quot;</span>);</span><br><span class="line">        <span class="built_in">this</span>.methodParam = methodParam;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">doFilterInternal</span><span class="params">(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)</span></span><br><span class="line">            <span class="keyword">throws</span> ServletException, IOException &#123;</span><br><span class="line"></span><br><span class="line">        <span class="type">HttpServletRequest</span> <span class="variable">requestToUse</span> <span class="operator">=</span> request;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (<span class="string">&quot;POST&quot;</span>.equals(request.getMethod()) &amp;&amp; request.getAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE) == <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="type">String</span> <span class="variable">paramValue</span> <span class="operator">=</span> request.getParameter(<span class="built_in">this</span>.methodParam);</span><br><span class="line">            <span class="keyword">if</span> (StringUtils.hasLength(paramValue)) &#123;</span><br><span class="line">                <span class="type">String</span> <span class="variable">method</span> <span class="operator">=</span> paramValue.toUpperCase(Locale.ENGLISH);</span><br><span class="line">                <span class="keyword">if</span> (ALLOWED_METHODS.contains(method)) &#123;</span><br><span class="line">                    requestToUse = <span class="keyword">new</span> <span class="title class_">HttpMethodRequestWrapper</span>(request, method);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        filterChain.doFilter(requestToUse, response);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Simple &#123;<span class="doctag">@link</span> HttpServletRequest&#125; wrapper that returns the supplied method for</span></span><br><span class="line"><span class="comment">     * &#123;<span class="doctag">@link</span> HttpServletRequest#getMethod()&#125;.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">HttpMethodRequestWrapper</span> <span class="keyword">extends</span> <span class="title class_">HttpServletRequestWrapper</span> &#123;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">final</span> String method;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">public</span> <span class="title function_">HttpMethodRequestWrapper</span><span class="params">(HttpServletRequest request, String method)</span> &#123;</span><br><span class="line">            <span class="built_in">super</span>(request);</span><br><span class="line">            <span class="built_in">this</span>.method = method;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="keyword">public</span> String <span class="title function_">getMethod</span><span class="params">()</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="built_in">this</span>.method;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<ul>
<li>Rest使用客户端工具。<ul>
<li>如PostMan可直接发送put、delete等方式请求。</li>
</ul>
</li>
</ul>
<h2 id="怎么改变默认的-method"><a href="#怎么改变默认的-method" class="headerlink" title="怎么改变默认的_method"></a>怎么改变默认的_method</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Configuration(proxyBeanMethods = false)</span></span><br><span class="line"><span class="meta">@ConditionalOnWebApplication(type = Type.SERVLET)</span></span><br><span class="line"><span class="meta">@ConditionalOnClass(&#123; Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class &#125;)</span></span><br><span class="line"><span class="meta">@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)</span></span><br><span class="line"><span class="meta">@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)</span></span><br><span class="line"><span class="meta">@AutoConfigureAfter(&#123; DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,</span></span><br><span class="line"><span class="meta">        ValidationAutoConfiguration.class &#125;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">WebMvcAutoConfiguration</span> &#123;</span><br><span class="line"></span><br><span class="line">    ...</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="meta">@ConditionalOnMissingBean(HiddenHttpMethodFilter.class)</span></span><br><span class="line">    <span class="meta">@ConditionalOnProperty(prefix = &quot;spring.mvc.hiddenmethod.filter&quot;, name = &quot;enabled&quot;, matchIfMissing = false)</span></span><br><span class="line">    <span class="keyword">public</span> OrderedHiddenHttpMethodFilter <span class="title function_">hiddenHttpMethodFilter</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">OrderedHiddenHttpMethodFilter</span>();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p><code>@ConditionalOnMissingBean(HiddenHttpMethodFilter.class)</code>意味着在没有<code>HiddenHttpMethodFilter</code>时，才执行<code>hiddenHttpMethodFilter()</code>。因此，我们可以自定义filter，改变默认的<code>\_method</code>。例如：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Configuration(proxyBeanMethods = false)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">WebConfig</span>&#123;</span><br><span class="line">    <span class="comment">//自定义filter</span></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> HiddenHttpMethodFilter <span class="title function_">hiddenHttpMethodFilter</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="type">HiddenHttpMethodFilter</span> <span class="variable">methodFilter</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">HiddenHttpMethodFilter</span>();</span><br><span class="line">        methodFilter.setMethodParam(<span class="string">&quot;_m&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> methodFilter;</span><br><span class="line">    &#125;    </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>将<code>\_method</code>改成<code>_m</code>。</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">form</span> <span class="attr">action</span>=<span class="string">&quot;/user&quot;</span> <span class="attr">method</span>=<span class="string">&quot;post&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">input</span> <span class="attr">name</span>=<span class="string">&quot;_m&quot;</span> <span class="attr">type</span>=<span class="string">&quot;hidden&quot;</span> <span class="attr">value</span>=<span class="string">&quot;DELETE&quot;</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">input</span> <span class="attr">value</span>=<span class="string">&quot;REST-DELETE 提交&quot;</span> <span class="attr">type</span>=<span class="string">&quot;submit&quot;</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">form</span>&gt;</span></span><br></pre></td></tr></table></figure>
<h2 id="请求映射原理"><a href="#请求映射原理" class="headerlink" title="请求映射原理"></a>请求映射原理</h2><center>
    <img style="border-radius: 0.3125em;
    box-shadow: 0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.08);" 
    src="https://gcore.jsdelivr.net/gh/shimmerjordan/pic_bed@main/blog/SpringBoot2/image/20210205005703527.png" width="70%">
    <br>
    <div style="color:orange; border-bottom: 1px solid #d9d9d9;
    display: inline-block;
    color: #999;
    padding: 2px;">请求映射</div>
</center>

<p>SpringMVC功能分析都从 <code>org.springframework.web.servlet.DispatcherServlet</code> -&gt; <code>doDispatch()</code></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">doDispatch</span><span class="params">(HttpServletRequest request, HttpServletResponse response)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">    <span class="type">HttpServletRequest</span> <span class="variable">processedRequest</span> <span class="operator">=</span> request;</span><br><span class="line">    <span class="type">HandlerExecutionChain</span> <span class="variable">mappedHandler</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">    <span class="type">boolean</span> <span class="variable">multipartRequestParsed</span> <span class="operator">=</span> <span class="literal">false</span>;</span><br><span class="line"></span><br><span class="line">    <span class="type">WebAsyncManager</span> <span class="variable">asyncManager</span> <span class="operator">=</span> WebAsyncUtils.getAsyncManager(request);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="type">ModelAndView</span> <span class="variable">mv</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">        <span class="type">Exception</span> <span class="variable">dispatchException</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            processedRequest = checkMultipart(request);</span><br><span class="line">            multipartRequestParsed = (processedRequest != request);</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 找到当前请求使用哪个Handler（Controller的方法）处理</span></span><br><span class="line">            mappedHandler = getHandler(processedRequest);</span><br><span class="line"></span><br><span class="line">            <span class="comment">//HandlerMapping：处理器映射。/xxx-&gt;&gt;xxxx</span></span><br><span class="line">    ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p><code>getHandler()</code>方法如下：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Nullable</span></span><br><span class="line"><span class="keyword">protected</span> HandlerExecutionChain <span class="title function_">getHandler</span><span class="params">(HttpServletRequest request)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">    <span class="keyword">if</span> (<span class="built_in">this</span>.handlerMappings != <span class="literal">null</span>) &#123;</span><br><span class="line">        <span class="keyword">for</span> (HandlerMapping mapping : <span class="built_in">this</span>.handlerMappings) &#123;</span><br><span class="line">            <span class="type">HandlerExecutionChain</span> <span class="variable">handler</span> <span class="operator">=</span> mapping.getHandler(request);</span><br><span class="line">            <span class="keyword">if</span> (handler != <span class="literal">null</span>) &#123;</span><br><span class="line">                <span class="keyword">return</span> handler;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p><code>this.handlerMappings</code>在Debug模式下展现的内容：</p>
<center>
    <img style="border-radius: 0.3125em;
    box-shadow: 0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.08);" 
    src="https://gcore.jsdelivr.net/gh/shimmerjordan/pic_bed@main/blog/SpringBoot2/image/20210205005802305.png" width="90%">
    <br>
    <div style="color:orange; border-bottom: 1px solid #d9d9d9;
    display: inline-block;
    color: #999;
    padding: 2px;">Debug模式</div>
</center>

<p>其中，保存了所有<code>@RequestMapping</code> 和<code>handler</code>的映射规则。</p>
<center>
    <img style="border-radius: 0.3125em;
    box-shadow: 0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.08);" 
    src="https://gcore.jsdelivr.net/gh/shimmerjordan/pic_bed@main/blog/SpringBoot2/image/20210205005926474.png" width="90%">
    <br>
    <div style="color:orange; border-bottom: 1px solid #d9d9d9;
    display: inline-block;
    color: #999;
    padding: 2px;">Debug模式</div>
</center>

<p>所有的请求映射都在HandlerMapping中：</p>
<ul>
<li><p>SpringBoot自动配置欢迎页的 WelcomePageHandlerMapping 。访问 /能访问到index.html；</p>
</li>
<li><p>SpringBoot自动配置了默认 的 RequestMappingHandlerMapping</p>
</li>
<li><p>请求进来，挨个尝试所有的HandlerMapping看是否有请求信息。</p>
<ul>
<li>如果有就找到这个请求对应的handler</li>
<li>如果没有就是下一个 HandlerMapping</li>
</ul>
</li>
<li><p>我们需要一些自定义的映射处理，我们也可以自己给容器中放<strong>HandlerMapping</strong>。自定义 <strong>HandlerMapping</strong></p>
</li>
</ul>
<hr>
<p>IDEA快捷键：</p>
<ul>
<li>Ctrl + Alt + U : 以UML的类图展现类有哪些继承类，派生类以及实现哪些接口。</li>
<li>Crtl + Alt + Shift + U : 同上，区别在于上条快捷键结果在新页展现，而本条快捷键结果在弹窗展现。</li>
<li>Ctrl + H : 以树形方式展现类层次结构图。</li>
</ul>
<h1 id="常用参数注解使用"><a href="#常用参数注解使用" class="headerlink" title="常用参数注解使用"></a>常用参数注解使用</h1><p>注解：</p>
<ul>
<li><code>@PathVariable</code> 路径变量</li>
<li><code>@RequestHeader</code> 获取请求头</li>
<li><code>@RequestParam</code> 获取请求参数（指问号后的参数，url?a=1&amp;b=2）</li>
<li><code>@CookieValue</code> 获取Cookie值</li>
<li><code>@RequestAttribute</code> 获取request域属性</li>
<li><code>@RequestBody</code> 获取请求体[POST]</li>
<li><code>@MatrixVariable</code> 矩阵变量</li>
<li><code>@ModelAttribute</code></li>
</ul>
<p>使用用例：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@RestController</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ParameterTestController</span> &#123;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">//  car/2/owner/zhangsan</span></span><br><span class="line">    <span class="meta">@GetMapping(&quot;/car/&#123;id&#125;/owner/&#123;username&#125;&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Map&lt;String,Object&gt; <span class="title function_">getCar</span><span class="params">(<span class="meta">@PathVariable(&quot;id&quot;)</span> Integer id,</span></span><br><span class="line"><span class="params">                                     <span class="meta">@PathVariable(&quot;username&quot;)</span> String name,</span></span><br><span class="line"><span class="params">                                     <span class="meta">@PathVariable</span> Map&lt;String,String&gt; pv,</span></span><br><span class="line"><span class="params">                                     <span class="meta">@RequestHeader(&quot;User-Agent&quot;)</span> String userAgent,</span></span><br><span class="line"><span class="params">                                     <span class="meta">@RequestHeader</span> Map&lt;String,String&gt; header,</span></span><br><span class="line"><span class="params">                                     <span class="meta">@RequestParam(&quot;age&quot;)</span> Integer age,</span></span><br><span class="line"><span class="params">                                     <span class="meta">@RequestParam(&quot;inters&quot;)</span> List&lt;String&gt; inters,</span></span><br><span class="line"><span class="params">                                     <span class="meta">@RequestParam</span> Map&lt;String,String&gt; params,</span></span><br><span class="line"><span class="params">                                     <span class="meta">@CookieValue(&quot;_ga&quot;)</span> String _ga,</span></span><br><span class="line"><span class="params">                                     <span class="meta">@CookieValue(&quot;_ga&quot;)</span> Cookie cookie)</span>&#123;</span><br><span class="line"></span><br><span class="line">        Map&lt;String,Object&gt; map = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;&gt;();</span><br><span class="line"></span><br><span class="line"><span class="comment">//        map.put(&quot;id&quot;,id);</span></span><br><span class="line"><span class="comment">//        map.put(&quot;name&quot;,name);</span></span><br><span class="line"><span class="comment">//        map.put(&quot;pv&quot;,pv);</span></span><br><span class="line"><span class="comment">//        map.put(&quot;userAgent&quot;,userAgent);</span></span><br><span class="line"><span class="comment">//        map.put(&quot;headers&quot;,header);</span></span><br><span class="line">        map.put(<span class="string">&quot;age&quot;</span>,age);</span><br><span class="line">        map.put(<span class="string">&quot;inters&quot;</span>,inters);</span><br><span class="line">        map.put(<span class="string">&quot;params&quot;</span>,params);</span><br><span class="line">        map.put(<span class="string">&quot;_ga&quot;</span>,_ga);</span><br><span class="line">        System.out.println(cookie.getName()+<span class="string">&quot;===&gt;&quot;</span>+cookie.getValue());</span><br><span class="line">        <span class="keyword">return</span> map;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="meta">@PostMapping(&quot;/save&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Map <span class="title function_">postMethod</span><span class="params">(<span class="meta">@RequestBody</span> String content)</span>&#123;</span><br><span class="line">        Map&lt;String,Object&gt; map = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;&gt;();</span><br><span class="line">        map.put(<span class="string">&quot;content&quot;</span>,content);</span><br><span class="line">        <span class="keyword">return</span> map;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="请求处理-RequestAttribute"><a href="#请求处理-RequestAttribute" class="headerlink" title="请求处理-@RequestAttribute"></a>请求处理-@RequestAttribute</h2><p>用例：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Controller</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">RequestController</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@GetMapping(&quot;/goto&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">goToPage</span><span class="params">(HttpServletRequest request)</span>&#123;</span><br><span class="line"></span><br><span class="line">        request.setAttribute(<span class="string">&quot;msg&quot;</span>,<span class="string">&quot;成功了...&quot;</span>);</span><br><span class="line">        request.setAttribute(<span class="string">&quot;code&quot;</span>,<span class="number">200</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;forward:/success&quot;</span>;  <span class="comment">//转发到  /success请求</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@GetMapping(&quot;/params&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">testParam</span><span class="params">(Map&lt;String,Object&gt; map,</span></span><br><span class="line"><span class="params">                            Model model,</span></span><br><span class="line"><span class="params">                            HttpServletRequest request,</span></span><br><span class="line"><span class="params">                            HttpServletResponse response)</span>&#123;</span><br><span class="line">        map.put(<span class="string">&quot;hello&quot;</span>,<span class="string">&quot;world666&quot;</span>);</span><br><span class="line">        model.addAttribute(<span class="string">&quot;world&quot;</span>,<span class="string">&quot;hello666&quot;</span>);</span><br><span class="line">        request.setAttribute(<span class="string">&quot;message&quot;</span>,<span class="string">&quot;HelloWorld&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="type">Cookie</span> <span class="variable">cookie</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Cookie</span>(<span class="string">&quot;c1&quot;</span>,<span class="string">&quot;v1&quot;</span>);</span><br><span class="line">        response.addCookie(cookie);</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;forward:/success&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">///&lt;-----------------主角@RequestAttribute在这个方法</span></span><br><span class="line">    <span class="meta">@ResponseBody</span></span><br><span class="line">    <span class="meta">@GetMapping(&quot;/success&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Map <span class="title function_">success</span><span class="params">(<span class="meta">@RequestAttribute(value = &quot;msg&quot;,required = false)</span> String msg,</span></span><br><span class="line"><span class="params">                       <span class="meta">@RequestAttribute(value = &quot;code&quot;,required = false)</span>Integer code,</span></span><br><span class="line"><span class="params">                       HttpServletRequest request)</span>&#123;</span><br><span class="line">        <span class="type">Object</span> <span class="variable">msg1</span> <span class="operator">=</span> request.getAttribute(<span class="string">&quot;msg&quot;</span>);</span><br><span class="line"></span><br><span class="line">        Map&lt;String,Object&gt; map = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;&gt;();</span><br><span class="line">        <span class="type">Object</span> <span class="variable">hello</span> <span class="operator">=</span> request.getAttribute(<span class="string">&quot;hello&quot;</span>);</span><br><span class="line">        <span class="type">Object</span> <span class="variable">world</span> <span class="operator">=</span> request.getAttribute(<span class="string">&quot;world&quot;</span>);</span><br><span class="line">        <span class="type">Object</span> <span class="variable">message</span> <span class="operator">=</span> request.getAttribute(<span class="string">&quot;message&quot;</span>);</span><br><span class="line"></span><br><span class="line">        map.put(<span class="string">&quot;reqMethod_msg&quot;</span>,msg1);</span><br><span class="line">        map.put(<span class="string">&quot;annotation_msg&quot;</span>,msg);</span><br><span class="line">        map.put(<span class="string">&quot;hello&quot;</span>,hello);</span><br><span class="line">        map.put(<span class="string">&quot;world&quot;</span>,world);</span><br><span class="line">        map.put(<span class="string">&quot;message&quot;</span>,message);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> map;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="请求处理-MatrixVariable与UrlPathHelper"><a href="#请求处理-MatrixVariable与UrlPathHelper" class="headerlink" title="请求处理-@MatrixVariable与UrlPathHelper"></a>请求处理-@MatrixVariable与UrlPathHelper</h2><ol>
<li><p>语法： 请求路径：<code>/cars/sell;low=34;brand=byd,audi,yd</code></p>
</li>
<li><p>SpringBoot默认是禁用了矩阵变量的功能</p>
<ul>
<li>手动开启：原理。对于路径的处理。UrlPathHelper的removeSemicolonContent设置为false，让其支持矩阵变量的。</li>
</ul>
</li>
<li><p>矩阵变量<strong>必须</strong>有url路径变量才能被解析</p>
</li>
</ol>
<p><strong>手动开启矩阵变量</strong>：</p>
<ul>
<li>实现<code>WebMvcConfigurer</code>接口：</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Configuration(proxyBeanMethods = false)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">WebConfig</span> <span class="keyword">implements</span> <span class="title class_">WebMvcConfigurer</span> &#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">configurePathMatch</span><span class="params">(PathMatchConfigurer configurer)</span> &#123;</span><br><span class="line"></span><br><span class="line">        <span class="type">UrlPathHelper</span> <span class="variable">urlPathHelper</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">UrlPathHelper</span>();</span><br><span class="line">        <span class="comment">// 不移除；后面的内容。矩阵变量功能就可以生效</span></span><br><span class="line">        urlPathHelper.setRemoveSemicolonContent(<span class="literal">false</span>);</span><br><span class="line">        configurer.setUrlPathHelper(urlPathHelper);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<ul>
<li>创建返回<code>WebMvcConfigurer</code>Bean：</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Configuration(proxyBeanMethods = false)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">WebConfig</span>&#123;</span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> WebMvcConfigurer <span class="title function_">webMvcConfigurer</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">WebMvcConfigurer</span>() &#123;</span><br><span class="line">                        <span class="meta">@Override</span></span><br><span class="line">            <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">configurePathMatch</span><span class="params">(PathMatchConfigurer configurer)</span> &#123;</span><br><span class="line">                <span class="type">UrlPathHelper</span> <span class="variable">urlPathHelper</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">UrlPathHelper</span>();</span><br><span class="line">                <span class="comment">// 不移除；后面的内容。矩阵变量功能就可以生效</span></span><br><span class="line">                urlPathHelper.setRemoveSemicolonContent(<span class="literal">false</span>);</span><br><span class="line">                configurer.setUrlPathHelper(urlPathHelper);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p><strong><code>@MatrixVariable</code>的用例</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@RestController</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ParameterTestController</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">///cars/sell;low=34;brand=byd,audi,yd</span></span><br><span class="line">    <span class="meta">@GetMapping(&quot;/cars/&#123;path&#125;&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Map <span class="title function_">carsSell</span><span class="params">(<span class="meta">@MatrixVariable(&quot;low&quot;)</span> Integer low,</span></span><br><span class="line"><span class="params">                        <span class="meta">@MatrixVariable(&quot;brand&quot;)</span> List&lt;String&gt; brand,</span></span><br><span class="line"><span class="params">                        <span class="meta">@PathVariable(&quot;path&quot;)</span> String path)</span>&#123;</span><br><span class="line">        Map&lt;String,Object&gt; map = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;&gt;();</span><br><span class="line"></span><br><span class="line">        map.put(<span class="string">&quot;low&quot;</span>,low);</span><br><span class="line">        map.put(<span class="string">&quot;brand&quot;</span>,brand);</span><br><span class="line">        map.put(<span class="string">&quot;path&quot;</span>,path);</span><br><span class="line">        <span class="keyword">return</span> map;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// /boss/1;age=20/2;age=10</span></span><br><span class="line"></span><br><span class="line">    <span class="meta">@GetMapping(&quot;/boss/&#123;bossId&#125;/&#123;empId&#125;&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Map <span class="title function_">boss</span><span class="params">(<span class="meta">@MatrixVariable(value = &quot;age&quot;,pathVar = &quot;bossId&quot;)</span> Integer bossAge,</span></span><br><span class="line"><span class="params">                    <span class="meta">@MatrixVariable(value = &quot;age&quot;,pathVar = &quot;empId&quot;)</span> Integer empAge)</span>&#123;</span><br><span class="line">        Map&lt;String,Object&gt; map = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;&gt;();</span><br><span class="line"></span><br><span class="line">        map.put(<span class="string">&quot;bossAge&quot;</span>,bossAge);</span><br><span class="line">        map.put(<span class="string">&quot;empAge&quot;</span>,empAge);</span><br><span class="line">        <span class="keyword">return</span> map;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="请求处理-各种类型参数解析原理"><a href="#请求处理-各种类型参数解析原理" class="headerlink" title="请求处理-各种类型参数解析原理"></a>请求处理-各种类型参数解析原理</h2><p>这要从<code>DispatcherServlet</code>开始说起：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">DispatcherServlet</span> <span class="keyword">extends</span> <span class="title class_">FrameworkServlet</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">doDispatch</span><span class="params">(HttpServletRequest request, HttpServletResponse response)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="type">HttpServletRequest</span> <span class="variable">processedRequest</span> <span class="operator">=</span> request;</span><br><span class="line">        <span class="type">HandlerExecutionChain</span> <span class="variable">mappedHandler</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">        <span class="type">boolean</span> <span class="variable">multipartRequestParsed</span> <span class="operator">=</span> <span class="literal">false</span>;</span><br><span class="line"></span><br><span class="line">        <span class="type">WebAsyncManager</span> <span class="variable">asyncManager</span> <span class="operator">=</span> WebAsyncUtils.getAsyncManager(request);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="type">ModelAndView</span> <span class="variable">mv</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">            <span class="type">Exception</span> <span class="variable">dispatchException</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                processedRequest = checkMultipart(request);</span><br><span class="line">                multipartRequestParsed = (processedRequest != request);</span><br><span class="line"></span><br><span class="line">                <span class="comment">// Determine handler for the current request.</span></span><br><span class="line">                mappedHandler = getHandler(processedRequest);</span><br><span class="line">                <span class="keyword">if</span> (mappedHandler == <span class="literal">null</span>) &#123;</span><br><span class="line">                    noHandlerFound(processedRequest, response);</span><br><span class="line">                    <span class="keyword">return</span>;</span><br><span class="line">                &#125;</span><br><span class="line"></span><br><span class="line">                <span class="comment">// Determine handler adapter for the current request.</span></span><br><span class="line">                <span class="type">HandlerAdapter</span> <span class="variable">ha</span> <span class="operator">=</span> getHandlerAdapter(mappedHandler.getHandler());</span><br><span class="line">                ...</span><br></pre></td></tr></table></figure>
<ul>
<li><code>HandlerMapping</code>中找到能处理请求的<code>Handler</code>（Controller.method()）。</li>
<li>为当前Handler 找一个适配器 <code>HandlerAdapter</code>，用的最多的是<strong>RequestMappingHandlerAdapter</strong>。</li>
<li>适配器执行目标方法并确定方法参数的每一个值。</li>
</ul>
<h3 id="HandlerAdapter"><a href="#HandlerAdapter" class="headerlink" title="HandlerAdapter"></a>HandlerAdapter</h3><p>默认会加载所有<code>HandlerAdapter</code></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">DispatcherServlet</span> <span class="keyword">extends</span> <span class="title class_">FrameworkServlet</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/** Detect all HandlerAdapters or just expect &quot;handlerAdapter&quot; bean?. */</span></span><br><span class="line">    <span class="keyword">private</span> <span class="type">boolean</span> <span class="variable">detectAllHandlerAdapters</span> <span class="operator">=</span> <span class="literal">true</span>;</span><br><span class="line"></span><br><span class="line">    ...</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">initHandlerAdapters</span><span class="params">(ApplicationContext context)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.handlerAdapters = <span class="literal">null</span>;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (<span class="built_in">this</span>.detectAllHandlerAdapters) &#123;</span><br><span class="line">            <span class="comment">// Find all HandlerAdapters in the ApplicationContext, including ancestor contexts.</span></span><br><span class="line">            Map&lt;String, HandlerAdapter&gt; matchingBeans =</span><br><span class="line">                BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, <span class="literal">true</span>, <span class="literal">false</span>);</span><br><span class="line">            <span class="keyword">if</span> (!matchingBeans.isEmpty()) &#123;</span><br><span class="line">                <span class="built_in">this</span>.handlerAdapters = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;(matchingBeans.values());</span><br><span class="line">                <span class="comment">// We keep HandlerAdapters in sorted order.</span></span><br><span class="line">                AnnotationAwareOrderComparator.sort(<span class="built_in">this</span>.handlerAdapters);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">     ...</span><br></pre></td></tr></table></figure>
<p>有这些<code>HandlerAdapter</code>：</p>
<center>
    <img style="border-radius: 0.3125em;
    box-shadow: 0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.08);" 
    src="https://gcore.jsdelivr.net/gh/shimmerjordan/pic_bed@main/blog/SpringBoot2/image/20210205010047654.png" width="70%">
    <br>
    <div style="color:orange; border-bottom: 1px solid #d9d9d9;
    display: inline-block;
    color: #999;
    padding: 2px;">HandlerAdapter</div>
</center>

<ol>
<li><p>支持方法上标注<code>@RequestMapping</code> </p>
</li>
<li><p>支持函数式编程的</p>
</li>
<li><p>…</p>
</li>
<li><p>…</p>
</li>
</ol>
<h3 id="执行目标方法"><a href="#执行目标方法" class="headerlink" title="执行目标方法"></a>执行目标方法</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">DispatcherServlet</span> <span class="keyword">extends</span> <span class="title class_">FrameworkServlet</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">doDispatch</span><span class="params">(HttpServletRequest request, HttpServletResponse response)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="type">ModelAndView</span> <span class="variable">mv</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line"></span><br><span class="line">        ...</span><br><span class="line"></span><br><span class="line">        <span class="comment">// Determine handler for the current request.</span></span><br><span class="line">        mappedHandler = getHandler(processedRequest);</span><br><span class="line">        <span class="keyword">if</span> (mappedHandler == <span class="literal">null</span>) &#123;</span><br><span class="line">            noHandlerFound(processedRequest, response);</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// Determine handler adapter for the current request.</span></span><br><span class="line">        <span class="type">HandlerAdapter</span> <span class="variable">ha</span> <span class="operator">=</span> getHandlerAdapter(mappedHandler.getHandler());</span><br><span class="line"></span><br><span class="line">        ...</span><br><span class="line">        <span class="comment">//本节重点</span></span><br><span class="line">        <span class="comment">// Actually invoke the handler.</span></span><br><span class="line">        mv = ha.handle(processedRequest, response, mappedHandler.getHandler());</span><br></pre></td></tr></table></figure>
<p><code>HandlerAdapter</code>接口实现类<code>RequestMappingHandlerAdapter</code>（主要用来处理<code>@RequestMapping</code>）</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">RequestMappingHandlerAdapter</span> <span class="keyword">extends</span> <span class="title class_">AbstractHandlerMethodAdapter</span></span><br><span class="line">        <span class="keyword">implements</span> <span class="title class_">BeanFactoryAware</span>, InitializingBean &#123;</span><br><span class="line"></span><br><span class="line">    ...</span><br><span class="line"></span><br><span class="line">    <span class="comment">//AbstractHandlerMethodAdapter类的方法，RequestMappingHandlerAdapter继承AbstractHandlerMethodAdapter</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">final</span> ModelAndView <span class="title function_">handle</span><span class="params">(HttpServletRequest request, HttpServletResponse response, Object handler)</span></span><br><span class="line">        <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> handleInternal(request, response, (HandlerMethod) handler);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> ModelAndView <span class="title function_">handleInternal</span><span class="params">(HttpServletRequest request,</span></span><br><span class="line"><span class="params">            HttpServletResponse response, HandlerMethod handlerMethod)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        ModelAndView mav;</span><br><span class="line">        <span class="comment">//handleInternal的核心</span></span><br><span class="line">        mav = invokeHandlerMethod(request, response, handlerMethod);<span class="comment">//解释看下节</span></span><br><span class="line">        <span class="comment">//...</span></span><br><span class="line">        <span class="keyword">return</span> mav;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h3 id="参数解析器"><a href="#参数解析器" class="headerlink" title="参数解析器"></a>参数解析器</h3><p>确定将要执行的目标方法的每一个参数的值是什么;</p>
<p>SpringMVC目标方法能写多少种参数类型。取决于<strong>参数解析器argumentResolvers</strong>。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Nullable</span></span><br><span class="line"><span class="keyword">protected</span> ModelAndView <span class="title function_">invokeHandlerMethod</span><span class="params">(HttpServletRequest request,</span></span><br><span class="line"><span class="params">                                           HttpServletResponse response, HandlerMethod handlerMethod)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">    <span class="type">ServletWebRequest</span> <span class="variable">webRequest</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ServletWebRequest</span>(request, response);</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="type">WebDataBinderFactory</span> <span class="variable">binderFactory</span> <span class="operator">=</span> getDataBinderFactory(handlerMethod);</span><br><span class="line">        <span class="type">ModelFactory</span> <span class="variable">modelFactory</span> <span class="operator">=</span> getModelFactory(handlerMethod, binderFactory);</span><br><span class="line"></span><br><span class="line">        <span class="type">ServletInvocableHandlerMethod</span> <span class="variable">invocableMethod</span> <span class="operator">=</span> createInvocableHandlerMethod(handlerMethod);</span><br><span class="line">        <span class="keyword">if</span> (<span class="built_in">this</span>.argumentResolvers != <span class="literal">null</span>) &#123;<span class="comment">//&lt;-----关注点</span></span><br><span class="line">            invocableMethod.setHandlerMethodArgumentResolvers(<span class="built_in">this</span>.argumentResolvers);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        ...</span><br></pre></td></tr></table></figure>
<p><code>this.argumentResolvers</code>在<code>afterPropertiesSet()</code>方法内初始化</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">RequestMappingHandlerAdapter</span> <span class="keyword">extends</span> <span class="title class_">AbstractHandlerMethodAdapter</span></span><br><span class="line">        <span class="keyword">implements</span> <span class="title class_">BeanFactoryAware</span>, InitializingBean &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Nullable</span></span><br><span class="line">    <span class="keyword">private</span> HandlerMethodArgumentResolverComposite argumentResolvers;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">afterPropertiesSet</span><span class="params">()</span> &#123;</span><br><span class="line">        ...</span><br><span class="line">        <span class="keyword">if</span> (<span class="built_in">this</span>.argumentResolvers == <span class="literal">null</span>) &#123;<span class="comment">//初始化argumentResolvers</span></span><br><span class="line">            List&lt;HandlerMethodArgumentResolver&gt; resolvers = getDefaultArgumentResolvers();</span><br><span class="line">            <span class="built_in">this</span>.argumentResolvers = <span class="keyword">new</span> <span class="title class_">HandlerMethodArgumentResolverComposite</span>().addResolvers(resolvers);</span><br><span class="line">        &#125;</span><br><span class="line">        ...</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//初始化了一堆的实现HandlerMethodArgumentResolver接口的</span></span><br><span class="line">    <span class="keyword">private</span> List&lt;HandlerMethodArgumentResolver&gt; <span class="title function_">getDefaultArgumentResolvers</span><span class="params">()</span> &#123;</span><br><span class="line">        List&lt;HandlerMethodArgumentResolver&gt; resolvers = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;(<span class="number">30</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// Annotation-based argument resolution</span></span><br><span class="line">        resolvers.add(<span class="keyword">new</span> <span class="title class_">RequestParamMethodArgumentResolver</span>(getBeanFactory(), <span class="literal">false</span>));</span><br><span class="line">        resolvers.add(<span class="keyword">new</span> <span class="title class_">RequestParamMapMethodArgumentResolver</span>());</span><br><span class="line">        resolvers.add(<span class="keyword">new</span> <span class="title class_">PathVariableMethodArgumentResolver</span>());</span><br><span class="line">        resolvers.add(<span class="keyword">new</span> <span class="title class_">PathVariableMapMethodArgumentResolver</span>());</span><br><span class="line">        resolvers.add(<span class="keyword">new</span> <span class="title class_">MatrixVariableMethodArgumentResolver</span>());</span><br><span class="line">        resolvers.add(<span class="keyword">new</span> <span class="title class_">MatrixVariableMapMethodArgumentResolver</span>());</span><br><span class="line">        resolvers.add(<span class="keyword">new</span> <span class="title class_">ServletModelAttributeMethodProcessor</span>(<span class="literal">false</span>));</span><br><span class="line">        resolvers.add(<span class="keyword">new</span> <span class="title class_">RequestResponseBodyMethodProcessor</span>(getMessageConverters(), <span class="built_in">this</span>.requestResponseBodyAdvice));</span><br><span class="line">        resolvers.add(<span class="keyword">new</span> <span class="title class_">RequestPartMethodArgumentResolver</span>(getMessageConverters(), <span class="built_in">this</span>.requestResponseBodyAdvice));</span><br><span class="line">        resolvers.add(<span class="keyword">new</span> <span class="title class_">RequestHeaderMethodArgumentResolver</span>(getBeanFactory()));</span><br><span class="line">        resolvers.add(<span class="keyword">new</span> <span class="title class_">RequestHeaderMapMethodArgumentResolver</span>());</span><br><span class="line">        resolvers.add(<span class="keyword">new</span> <span class="title class_">ServletCookieValueMethodArgumentResolver</span>(getBeanFactory()));</span><br><span class="line">        resolvers.add(<span class="keyword">new</span> <span class="title class_">ExpressionValueMethodArgumentResolver</span>(getBeanFactory()));</span><br><span class="line">        resolvers.add(<span class="keyword">new</span> <span class="title class_">SessionAttributeMethodArgumentResolver</span>());</span><br><span class="line">        resolvers.add(<span class="keyword">new</span> <span class="title class_">RequestAttributeMethodArgumentResolver</span>());</span><br><span class="line"></span><br><span class="line">        <span class="comment">// Type-based argument resolution</span></span><br><span class="line">        resolvers.add(<span class="keyword">new</span> <span class="title class_">ServletRequestMethodArgumentResolver</span>());</span><br><span class="line">        resolvers.add(<span class="keyword">new</span> <span class="title class_">ServletResponseMethodArgumentResolver</span>());</span><br><span class="line">        resolvers.add(<span class="keyword">new</span> <span class="title class_">HttpEntityMethodProcessor</span>(getMessageConverters(), <span class="built_in">this</span>.requestResponseBodyAdvice));</span><br><span class="line">        resolvers.add(<span class="keyword">new</span> <span class="title class_">RedirectAttributesMethodArgumentResolver</span>());</span><br><span class="line">        resolvers.add(<span class="keyword">new</span> <span class="title class_">ModelMethodProcessor</span>());</span><br><span class="line">        resolvers.add(<span class="keyword">new</span> <span class="title class_">MapMethodProcessor</span>());</span><br><span class="line">        resolvers.add(<span class="keyword">new</span> <span class="title class_">ErrorsMethodArgumentResolver</span>());</span><br><span class="line">        resolvers.add(<span class="keyword">new</span> <span class="title class_">SessionStatusMethodArgumentResolver</span>());</span><br><span class="line">        resolvers.add(<span class="keyword">new</span> <span class="title class_">UriComponentsBuilderMethodArgumentResolver</span>());</span><br><span class="line">        <span class="keyword">if</span> (KotlinDetector.isKotlinPresent()) &#123;</span><br><span class="line">            resolvers.add(<span class="keyword">new</span> <span class="title class_">ContinuationHandlerMethodArgumentResolver</span>());</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// Custom arguments</span></span><br><span class="line">        <span class="keyword">if</span> (getCustomArgumentResolvers() != <span class="literal">null</span>) &#123;</span><br><span class="line">            resolvers.addAll(getCustomArgumentResolvers());</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// Catch-all</span></span><br><span class="line">        resolvers.add(<span class="keyword">new</span> <span class="title class_">PrincipalMethodArgumentResolver</span>());</span><br><span class="line">        resolvers.add(<span class="keyword">new</span> <span class="title class_">RequestParamMethodArgumentResolver</span>(getBeanFactory(), <span class="literal">true</span>));</span><br><span class="line">        resolvers.add(<span class="keyword">new</span> <span class="title class_">ServletModelAttributeMethodProcessor</span>(<span class="literal">true</span>));</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> resolvers;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p><code>HandlerMethodArgumentResolverComposite</code>类如下：（众多<strong>参数解析器argumentResolvers</strong>的包装类）。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">HandlerMethodArgumentResolverComposite</span> <span class="keyword">implements</span> <span class="title class_">HandlerMethodArgumentResolver</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> List&lt;HandlerMethodArgumentResolver&gt; argumentResolvers = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line"></span><br><span class="line">    ...</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> HandlerMethodArgumentResolverComposite <span class="title function_">addResolvers</span><span class="params">(</span></span><br><span class="line"><span class="params">            <span class="meta">@Nullable</span> HandlerMethodArgumentResolver... resolvers)</span> &#123;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (resolvers != <span class="literal">null</span>) &#123;</span><br><span class="line">            Collections.addAll(<span class="built_in">this</span>.argumentResolvers, resolvers);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">this</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>我们看看<code>HandlerMethodArgumentResolver</code>的源码：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">HandlerMethodArgumentResolver</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//当前解析器是否支持解析这种参数</span></span><br><span class="line">    <span class="type">boolean</span> <span class="title function_">supportsParameter</span><span class="params">(MethodParameter parameter)</span>;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Nullable</span><span class="comment">//如果支持，就调用 resolveArgument</span></span><br><span class="line">    Object <span class="title function_">resolveArgument</span><span class="params">(MethodParameter parameter, <span class="meta">@Nullable</span> ModelAndViewContainer mavContainer,</span></span><br><span class="line"><span class="params">            NativeWebRequest webRequest, <span class="meta">@Nullable</span> WebDataBinderFactory binderFactory)</span> <span class="keyword">throws</span> Exception;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h3 id="返回值处理器"><a href="#返回值处理器" class="headerlink" title="返回值处理器"></a>返回值处理器</h3><p><strong>ValueHandler</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Nullable</span></span><br><span class="line"><span class="keyword">protected</span> ModelAndView <span class="title function_">invokeHandlerMethod</span><span class="params">(HttpServletRequest request,</span></span><br><span class="line"><span class="params">                                           HttpServletResponse response, HandlerMethod handlerMethod)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">    <span class="type">ServletWebRequest</span> <span class="variable">webRequest</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ServletWebRequest</span>(request, response);</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="type">WebDataBinderFactory</span> <span class="variable">binderFactory</span> <span class="operator">=</span> getDataBinderFactory(handlerMethod);</span><br><span class="line">        <span class="type">ModelFactory</span> <span class="variable">modelFactory</span> <span class="operator">=</span> getModelFactory(handlerMethod, binderFactory);</span><br><span class="line"></span><br><span class="line">        <span class="type">ServletInvocableHandlerMethod</span> <span class="variable">invocableMethod</span> <span class="operator">=</span> createInvocableHandlerMethod(handlerMethod);</span><br><span class="line">        <span class="keyword">if</span> (<span class="built_in">this</span>.argumentResolvers != <span class="literal">null</span>) &#123;</span><br><span class="line">            invocableMethod.setHandlerMethodArgumentResolvers(<span class="built_in">this</span>.argumentResolvers);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (<span class="built_in">this</span>.returnValueHandlers != <span class="literal">null</span>) &#123;<span class="comment">//&lt;---关注点</span></span><br><span class="line">            invocableMethod.setHandlerMethodReturnValueHandlers(<span class="built_in">this</span>.returnValueHandlers);</span><br><span class="line">        &#125;</span><br><span class="line">     ...</span><br></pre></td></tr></table></figure>
<p><code>this.returnValueHandlers</code>在<code>afterPropertiesSet()</code>方法内初始化</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">RequestMappingHandlerAdapter</span> <span class="keyword">extends</span> <span class="title class_">AbstractHandlerMethodAdapter</span></span><br><span class="line">        <span class="keyword">implements</span> <span class="title class_">BeanFactoryAware</span>, InitializingBean &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Nullable</span></span><br><span class="line">    <span class="keyword">private</span> HandlerMethodReturnValueHandlerComposite returnValueHandlers;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">afterPropertiesSet</span><span class="params">()</span> &#123;</span><br><span class="line"></span><br><span class="line">        ...</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (<span class="built_in">this</span>.returnValueHandlers == <span class="literal">null</span>) &#123;</span><br><span class="line">            List&lt;HandlerMethodReturnValueHandler&gt; handlers = getDefaultReturnValueHandlers();</span><br><span class="line">            <span class="built_in">this</span>.returnValueHandlers = <span class="keyword">new</span> <span class="title class_">HandlerMethodReturnValueHandlerComposite</span>().addHandlers(handlers);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//初始化了一堆的实现HandlerMethodReturnValueHandler接口的</span></span><br><span class="line">    <span class="keyword">private</span> List&lt;HandlerMethodReturnValueHandler&gt; <span class="title function_">getDefaultReturnValueHandlers</span><span class="params">()</span> &#123;</span><br><span class="line">        List&lt;HandlerMethodReturnValueHandler&gt; handlers = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;(<span class="number">20</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// Single-purpose return value types</span></span><br><span class="line">        handlers.add(<span class="keyword">new</span> <span class="title class_">ModelAndViewMethodReturnValueHandler</span>());</span><br><span class="line">        handlers.add(<span class="keyword">new</span> <span class="title class_">ModelMethodProcessor</span>());</span><br><span class="line">        handlers.add(<span class="keyword">new</span> <span class="title class_">ViewMethodReturnValueHandler</span>());</span><br><span class="line">        handlers.add(<span class="keyword">new</span> <span class="title class_">ResponseBodyEmitterReturnValueHandler</span>(getMessageConverters(),</span><br><span class="line">                <span class="built_in">this</span>.reactiveAdapterRegistry, <span class="built_in">this</span>.taskExecutor, <span class="built_in">this</span>.contentNegotiationManager));</span><br><span class="line">        handlers.add(<span class="keyword">new</span> <span class="title class_">StreamingResponseBodyReturnValueHandler</span>());</span><br><span class="line">        handlers.add(<span class="keyword">new</span> <span class="title class_">HttpEntityMethodProcessor</span>(getMessageConverters(),</span><br><span class="line">                <span class="built_in">this</span>.contentNegotiationManager, <span class="built_in">this</span>.requestResponseBodyAdvice));</span><br><span class="line">        handlers.add(<span class="keyword">new</span> <span class="title class_">HttpHeadersReturnValueHandler</span>());</span><br><span class="line">        handlers.add(<span class="keyword">new</span> <span class="title class_">CallableMethodReturnValueHandler</span>());</span><br><span class="line">        handlers.add(<span class="keyword">new</span> <span class="title class_">DeferredResultMethodReturnValueHandler</span>());</span><br><span class="line">        handlers.add(<span class="keyword">new</span> <span class="title class_">AsyncTaskMethodReturnValueHandler</span>(<span class="built_in">this</span>.beanFactory));</span><br><span class="line"></span><br><span class="line">        <span class="comment">// Annotation-based return value types</span></span><br><span class="line">        handlers.add(<span class="keyword">new</span> <span class="title class_">ServletModelAttributeMethodProcessor</span>(<span class="literal">false</span>));</span><br><span class="line">        handlers.add(<span class="keyword">new</span> <span class="title class_">RequestResponseBodyMethodProcessor</span>(getMessageConverters(),</span><br><span class="line">                <span class="built_in">this</span>.contentNegotiationManager, <span class="built_in">this</span>.requestResponseBodyAdvice));</span><br><span class="line"></span><br><span class="line">        <span class="comment">// Multi-purpose return value types</span></span><br><span class="line">        handlers.add(<span class="keyword">new</span> <span class="title class_">ViewNameMethodReturnValueHandler</span>());</span><br><span class="line">        handlers.add(<span class="keyword">new</span> <span class="title class_">MapMethodProcessor</span>());</span><br><span class="line"></span><br><span class="line">        <span class="comment">// Custom return value types</span></span><br><span class="line">        <span class="keyword">if</span> (getCustomReturnValueHandlers() != <span class="literal">null</span>) &#123;</span><br><span class="line">            handlers.addAll(getCustomReturnValueHandlers());</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// Catch-all</span></span><br><span class="line">        <span class="keyword">if</span> (!CollectionUtils.isEmpty(getModelAndViewResolvers())) &#123;</span><br><span class="line">            handlers.add(<span class="keyword">new</span> <span class="title class_">ModelAndViewResolverMethodReturnValueHandler</span>(getModelAndViewResolvers()));</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span> &#123;</span><br><span class="line">            handlers.add(<span class="keyword">new</span> <span class="title class_">ServletModelAttributeMethodProcessor</span>(<span class="literal">true</span>));</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> handlers;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p><code>HandlerMethodReturnValueHandlerComposite</code>类如下：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">HandlerMethodReturnValueHandlerComposite</span> <span class="keyword">implements</span> <span class="title class_">HandlerMethodReturnValueHandler</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> List&lt;HandlerMethodReturnValueHandler&gt; returnValueHandlers = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line"></span><br><span class="line">    ...</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> HandlerMethodReturnValueHandlerComposite <span class="title function_">addHandlers</span><span class="params">(</span></span><br><span class="line"><span class="params">            <span class="meta">@Nullable</span> List&lt;? extends HandlerMethodReturnValueHandler&gt; handlers)</span> &#123;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (handlers != <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="built_in">this</span>.returnValueHandlers.addAll(handlers);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">this</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p><code>HandlerMethodReturnValueHandler</code>接口：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">HandlerMethodReturnValueHandler</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="type">boolean</span> <span class="title function_">supportsReturnType</span><span class="params">(MethodParameter returnType)</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">handleReturnValue</span><span class="params">(<span class="meta">@Nullable</span> Object returnValue, MethodParameter returnType,</span></span><br><span class="line"><span class="params">            ModelAndViewContainer mavContainer, NativeWebRequest webRequest)</span> <span class="keyword">throws</span> Exception;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h3 id="回顾执行目标方法"><a href="#回顾执行目标方法" class="headerlink" title="回顾执行目标方法"></a>回顾执行目标方法</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">DispatcherServlet</span> <span class="keyword">extends</span> <span class="title class_">FrameworkServlet</span> &#123;</span><br><span class="line">    ...</span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">doDispatch</span><span class="params">(HttpServletRequest request, HttpServletResponse response)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="type">ModelAndView</span> <span class="variable">mv</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">        ...</span><br><span class="line">        mv = ha.handle(processedRequest, response, mappedHandler.getHandler());</span><br></pre></td></tr></table></figure>
<p><code>RequestMappingHandlerAdapter</code>的<code>handle()</code>方法：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">RequestMappingHandlerAdapter</span> <span class="keyword">extends</span> <span class="title class_">AbstractHandlerMethodAdapter</span></span><br><span class="line">        <span class="keyword">implements</span> <span class="title class_">BeanFactoryAware</span>, InitializingBean &#123;</span><br><span class="line"></span><br><span class="line">    ...</span><br><span class="line"></span><br><span class="line">    <span class="comment">//AbstractHandlerMethodAdapter类的方法，RequestMappingHandlerAdapter继承AbstractHandlerMethodAdapter</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">final</span> ModelAndView <span class="title function_">handle</span><span class="params">(HttpServletRequest request, HttpServletResponse response, Object handler)</span></span><br><span class="line">        <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> handleInternal(request, response, (HandlerMethod) handler);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> ModelAndView <span class="title function_">handleInternal</span><span class="params">(HttpServletRequest request,</span></span><br><span class="line"><span class="params">            HttpServletResponse response, HandlerMethod handlerMethod)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        ModelAndView mav;</span><br><span class="line">        <span class="comment">//handleInternal的核心</span></span><br><span class="line">        mav = invokeHandlerMethod(request, response, handlerMethod);<span class="comment">//解释看下节</span></span><br><span class="line">        <span class="comment">//...</span></span><br><span class="line">        <span class="keyword">return</span> mav;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p><code>RequestMappingHandlerAdapter</code>的<code>invokeHandlerMethod()</code>方法：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">RequestMappingHandlerAdapter</span> <span class="keyword">extends</span> <span class="title class_">AbstractHandlerMethodAdapter</span></span><br><span class="line">        <span class="keyword">implements</span> <span class="title class_">BeanFactoryAware</span>, InitializingBean &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">protected</span> ModelAndView <span class="title function_">invokeHandlerMethod</span><span class="params">(HttpServletRequest request,</span></span><br><span class="line"><span class="params">            HttpServletResponse response, HandlerMethod handlerMethod)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">        <span class="type">ServletWebRequest</span> <span class="variable">webRequest</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ServletWebRequest</span>(request, response);</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            ...</span><br><span class="line"></span><br><span class="line">            <span class="type">ServletInvocableHandlerMethod</span> <span class="variable">invocableMethod</span> <span class="operator">=</span> createInvocableHandlerMethod(handlerMethod);</span><br><span class="line">            <span class="keyword">if</span> (<span class="built_in">this</span>.argumentResolvers != <span class="literal">null</span>) &#123;</span><br><span class="line">                invocableMethod.setHandlerMethodArgumentResolvers(<span class="built_in">this</span>.argumentResolvers);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (<span class="built_in">this</span>.returnValueHandlers != <span class="literal">null</span>) &#123;</span><br><span class="line">                invocableMethod.setHandlerMethodReturnValueHandlers(<span class="built_in">this</span>.returnValueHandlers);</span><br><span class="line">            &#125;</span><br><span class="line">            ...</span><br><span class="line"></span><br><span class="line">            <span class="comment">//关注点：执行目标方法</span></span><br><span class="line">            invocableMethod.invokeAndHandle(webRequest, mavContainer);</span><br><span class="line">            <span class="keyword">if</span> (asyncManager.isConcurrentHandlingStarted()) &#123;</span><br><span class="line">                <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">return</span> getModelAndView(mavContainer, modelFactory, webRequest);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">finally</span> &#123;</span><br><span class="line">            webRequest.requestCompleted();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>
<p><code>invokeAndHandle()</code>方法如下：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ServletInvocableHandlerMethod</span> <span class="keyword">extends</span> <span class="title class_">InvocableHandlerMethod</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">invokeAndHandle</span><span class="params">(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,</span></span><br><span class="line"><span class="params">            Object... providedArgs)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">        <span class="type">Object</span> <span class="variable">returnValue</span> <span class="operator">=</span> invokeForRequest(webRequest, mavContainer, providedArgs);</span><br><span class="line"></span><br><span class="line">        ...</span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">//returnValue存储起来</span></span><br><span class="line">            <span class="built_in">this</span>.returnValueHandlers.handleReturnValue(</span><br><span class="line">                    returnValue, getReturnValueType(returnValue), mavContainer, webRequest);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">catch</span> (Exception ex) &#123;</span><br><span class="line">            ...</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Nullable</span><span class="comment">//InvocableHandlerMethod类的，ServletInvocableHandlerMethod类继承InvocableHandlerMethod类</span></span><br><span class="line">    <span class="keyword">public</span> Object <span class="title function_">invokeForRequest</span><span class="params">(NativeWebRequest request, <span class="meta">@Nullable</span> ModelAndViewContainer mavContainer,</span></span><br><span class="line"><span class="params">            Object... providedArgs)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">////获取方法的参数值</span></span><br><span class="line">        Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);</span><br><span class="line"></span><br><span class="line">        ...</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> doInvoke(args);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Nullable</span></span><br><span class="line">    <span class="keyword">protected</span> Object <span class="title function_">doInvoke</span><span class="params">(Object... args)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="type">Method</span> <span class="variable">method</span> <span class="operator">=</span> getBridgedMethod();<span class="comment">//@RequestMapping的方法</span></span><br><span class="line">        ReflectionUtils.makeAccessible(method);</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">if</span> (KotlinDetector.isSuspendingFunction(method)) &#123;</span><br><span class="line">                <span class="keyword">return</span> CoroutinesUtils.invokeSuspendingFunction(method, getBean(), args);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="comment">//通过反射调用</span></span><br><span class="line">            <span class="keyword">return</span> method.invoke(getBean(), args);<span class="comment">//getBean()指@RequestMapping的方法所在类的对象。</span></span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">catch</span> (IllegalArgumentException ex) &#123;</span><br><span class="line">            ...</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">catch</span> (InvocationTargetException ex) &#123;</span><br><span class="line">            ...</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;   </span><br></pre></td></tr></table></figure>
<h3 id="如何确定目标方法每一个参数的值"><a href="#如何确定目标方法每一个参数的值" class="headerlink" title="如何确定目标方法每一个参数的值"></a>如何确定目标方法每一个参数的值</h3><p>重点分析<code>ServletInvocableHandlerMethod</code>的<code>getMethodArgumentValues</code>方法</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ServletInvocableHandlerMethod</span> <span class="keyword">extends</span> <span class="title class_">InvocableHandlerMethod</span> &#123;</span><br><span class="line">    ...</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Nullable</span><span class="comment">//InvocableHandlerMethod类的，ServletInvocableHandlerMethod类继承InvocableHandlerMethod类</span></span><br><span class="line">    <span class="keyword">public</span> Object <span class="title function_">invokeForRequest</span><span class="params">(NativeWebRequest request, <span class="meta">@Nullable</span> ModelAndViewContainer mavContainer,</span></span><br><span class="line"><span class="params">            Object... providedArgs)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">////获取方法的参数值</span></span><br><span class="line">        Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);</span><br><span class="line"></span><br><span class="line">        ...</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> doInvoke(args);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//本节重点，获取方法的参数值</span></span><br><span class="line">    <span class="keyword">protected</span> Object[] getMethodArgumentValues(NativeWebRequest request, <span class="meta">@Nullable</span> ModelAndViewContainer mavContainer,</span><br><span class="line">            Object... providedArgs) <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">        MethodParameter[] parameters = getMethodParameters();</span><br><span class="line">        <span class="keyword">if</span> (ObjectUtils.isEmpty(parameters)) &#123;</span><br><span class="line">            <span class="keyword">return</span> EMPTY_ARGS;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        Object[] args = <span class="keyword">new</span> <span class="title class_">Object</span>[parameters.length];</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; parameters.length; i++) &#123;</span><br><span class="line">            <span class="type">MethodParameter</span> <span class="variable">parameter</span> <span class="operator">=</span> parameters[i];</span><br><span class="line">            parameter.initParameterNameDiscovery(<span class="built_in">this</span>.parameterNameDiscoverer);</span><br><span class="line">            args[i] = findProvidedArgument(parameter, providedArgs);</span><br><span class="line">            <span class="keyword">if</span> (args[i] != <span class="literal">null</span>) &#123;</span><br><span class="line">                <span class="keyword">continue</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="comment">//查看resolvers是否有支持</span></span><br><span class="line">            <span class="keyword">if</span> (!<span class="built_in">this</span>.resolvers.supportsParameter(parameter)) &#123;</span><br><span class="line">                <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalStateException</span>(formatArgumentError(parameter, <span class="string">&quot;No suitable resolver&quot;</span>));</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="comment">//支持的话就开始解析吧</span></span><br><span class="line">                args[i] = <span class="built_in">this</span>.resolvers.resolveArgument(parameter, mavContainer, request, <span class="built_in">this</span>.dataBinderFactory);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">catch</span> (Exception ex) &#123;</span><br><span class="line">                ....</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> args;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p><code>this.resolvers</code>的类型为<code>HandlerMethodArgumentResolverComposite</code>（在<a href="#">参数解析器</a>章节提及）</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">HandlerMethodArgumentResolverComposite</span> <span class="keyword">implements</span> <span class="title class_">HandlerMethodArgumentResolver</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">supportsParameter</span><span class="params">(MethodParameter parameter)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> getArgumentResolver(parameter) != <span class="literal">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="meta">@Nullable</span></span><br><span class="line">    <span class="keyword">public</span> Object <span class="title function_">resolveArgument</span><span class="params">(MethodParameter parameter, <span class="meta">@Nullable</span> ModelAndViewContainer mavContainer,</span></span><br><span class="line"><span class="params">            NativeWebRequest webRequest, <span class="meta">@Nullable</span> WebDataBinderFactory binderFactory)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">        <span class="type">HandlerMethodArgumentResolver</span> <span class="variable">resolver</span> <span class="operator">=</span> getArgumentResolver(parameter);</span><br><span class="line">        <span class="keyword">if</span> (resolver == <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalArgumentException</span>(<span class="string">&quot;Unsupported parameter type [&quot;</span> +</span><br><span class="line">                    parameter.getParameterType().getName() + <span class="string">&quot;]. supportsParameter should be called first.&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="meta">@Nullable</span></span><br><span class="line">    <span class="keyword">private</span> HandlerMethodArgumentResolver <span class="title function_">getArgumentResolver</span><span class="params">(MethodParameter parameter)</span> &#123;</span><br><span class="line">        <span class="type">HandlerMethodArgumentResolver</span> <span class="variable">result</span> <span class="operator">=</span> <span class="built_in">this</span>.argumentResolverCache.get(parameter);</span><br><span class="line">        <span class="keyword">if</span> (result == <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="comment">//挨个判断所有参数解析器那个支持解析这个参数</span></span><br><span class="line">            <span class="keyword">for</span> (HandlerMethodArgumentResolver resolver : <span class="built_in">this</span>.argumentResolvers) &#123;</span><br><span class="line">                <span class="keyword">if</span> (resolver.supportsParameter(parameter)) &#123;</span><br><span class="line">                    result = resolver;</span><br><span class="line">                    <span class="built_in">this</span>.argumentResolverCache.put(parameter, result);<span class="comment">//找到了，resolver就缓存起来，方便稍后resolveArgument()方法使用</span></span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> result;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="小结"><a href="#小结" class="headerlink" title="小结"></a>小结</h2><p>本节描述，一个请求发送到DispatcherServlet后的具体处理流程，也就是SpringMVC的主要原理。</p>
<p>本节内容较多且硬核，对日后编程很有帮助，需耐心对待。</p>
<p>可以运行一个示例，打断点，在Debug模式下，查看程序流程。</p>
<h1 id="Servlet-API参数解析原理"><a href="#Servlet-API参数解析原理" class="headerlink" title="Servlet API参数解析原理"></a>Servlet API参数解析原理</h1><ul>
<li>WebRequest</li>
<li>ServletRequest</li>
<li>MultipartRequest</li>
<li>HttpSession</li>
<li>javax.servlet.http.PushBuilder</li>
<li>Principal</li>
<li>InputStream</li>
<li>Reader</li>
<li>HttpMethod</li>
<li>Locale</li>
<li>TimeZone</li>
<li>ZoneId</li>
</ul>
<p><strong>ServletRequestMethodArgumentResolver</strong>用来处理以上的参数</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ServletRequestMethodArgumentResolver</span> <span class="keyword">implements</span> <span class="title class_">HandlerMethodArgumentResolver</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Nullable</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> Class&lt;?&gt; pushBuilder;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            pushBuilder = ClassUtils.forName(<span class="string">&quot;javax.servlet.http.PushBuilder&quot;</span>,</span><br><span class="line">                    ServletRequestMethodArgumentResolver.class.getClassLoader());</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">catch</span> (ClassNotFoundException ex) &#123;</span><br><span class="line">            <span class="comment">// Servlet 4.0 PushBuilder not found - not supported for injection</span></span><br><span class="line">            pushBuilder = <span class="literal">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">supportsParameter</span><span class="params">(MethodParameter parameter)</span> &#123;</span><br><span class="line">        Class&lt;?&gt; paramType = parameter.getParameterType();</span><br><span class="line">        <span class="keyword">return</span> (WebRequest.class.isAssignableFrom(paramType) ||</span><br><span class="line">                ServletRequest.class.isAssignableFrom(paramType) ||</span><br><span class="line">                MultipartRequest.class.isAssignableFrom(paramType) ||</span><br><span class="line">                HttpSession.class.isAssignableFrom(paramType) ||</span><br><span class="line">                (pushBuilder != <span class="literal">null</span> &amp;&amp; pushBuilder.isAssignableFrom(paramType)) ||</span><br><span class="line">                (Principal.class.isAssignableFrom(paramType) &amp;&amp; !parameter.hasParameterAnnotations()) ||</span><br><span class="line">                InputStream.class.isAssignableFrom(paramType) ||</span><br><span class="line">                Reader.class.isAssignableFrom(paramType) ||</span><br><span class="line">                HttpMethod.class == paramType ||</span><br><span class="line">                Locale.class == paramType ||</span><br><span class="line">                TimeZone.class == paramType ||</span><br><span class="line">                ZoneId.class == paramType);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> Object <span class="title function_">resolveArgument</span><span class="params">(MethodParameter parameter, <span class="meta">@Nullable</span> ModelAndViewContainer mavContainer,</span></span><br><span class="line"><span class="params">            NativeWebRequest webRequest, <span class="meta">@Nullable</span> WebDataBinderFactory binderFactory)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">        Class&lt;?&gt; paramType = parameter.getParameterType();</span><br><span class="line"></span><br><span class="line">        <span class="comment">// WebRequest / NativeWebRequest / ServletWebRequest</span></span><br><span class="line">        <span class="keyword">if</span> (WebRequest.class.isAssignableFrom(paramType)) &#123;</span><br><span class="line">            <span class="keyword">if</span> (!paramType.isInstance(webRequest)) &#123;</span><br><span class="line">                <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalStateException</span>(</span><br><span class="line">                        <span class="string">&quot;Current request is not of type [&quot;</span> + paramType.getName() + <span class="string">&quot;]: &quot;</span> + webRequest);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">return</span> webRequest;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// ServletRequest / HttpServletRequest / MultipartRequest / MultipartHttpServletRequest</span></span><br><span class="line">        <span class="keyword">if</span> (ServletRequest.class.isAssignableFrom(paramType) || MultipartRequest.class.isAssignableFrom(paramType)) &#123;</span><br><span class="line">            <span class="keyword">return</span> resolveNativeRequest(webRequest, paramType);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// HttpServletRequest required for all further argument types</span></span><br><span class="line">        <span class="keyword">return</span> resolveArgument(paramType, resolveNativeRequest(webRequest, HttpServletRequest.class));</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> &lt;T&gt; T <span class="title function_">resolveNativeRequest</span><span class="params">(NativeWebRequest webRequest, Class&lt;T&gt; requiredType)</span> &#123;</span><br><span class="line">        <span class="type">T</span> <span class="variable">nativeRequest</span> <span class="operator">=</span> webRequest.getNativeRequest(requiredType);</span><br><span class="line">        <span class="keyword">if</span> (nativeRequest == <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalStateException</span>(</span><br><span class="line">                    <span class="string">&quot;Current request is not of type [&quot;</span> + requiredType.getName() + <span class="string">&quot;]: &quot;</span> + webRequest);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> nativeRequest;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Nullable</span></span><br><span class="line">    <span class="keyword">private</span> Object <span class="title function_">resolveArgument</span><span class="params">(Class&lt;?&gt; paramType, HttpServletRequest request)</span> <span class="keyword">throws</span> IOException &#123;</span><br><span class="line">        <span class="keyword">if</span> (HttpSession.class.isAssignableFrom(paramType)) &#123;</span><br><span class="line">            <span class="type">HttpSession</span> <span class="variable">session</span> <span class="operator">=</span> request.getSession();</span><br><span class="line">            <span class="keyword">if</span> (session != <span class="literal">null</span> &amp;&amp; !paramType.isInstance(session)) &#123;</span><br><span class="line">                <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalStateException</span>(</span><br><span class="line">                        <span class="string">&quot;Current session is not of type [&quot;</span> + paramType.getName() + <span class="string">&quot;]: &quot;</span> + session);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">return</span> session;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> (pushBuilder != <span class="literal">null</span> &amp;&amp; pushBuilder.isAssignableFrom(paramType)) &#123;</span><br><span class="line">            <span class="keyword">return</span> PushBuilderDelegate.resolvePushBuilder(request, paramType);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> (InputStream.class.isAssignableFrom(paramType)) &#123;</span><br><span class="line">            <span class="type">InputStream</span> <span class="variable">inputStream</span> <span class="operator">=</span> request.getInputStream();</span><br><span class="line">            <span class="keyword">if</span> (inputStream != <span class="literal">null</span> &amp;&amp; !paramType.isInstance(inputStream)) &#123;</span><br><span class="line">                <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalStateException</span>(</span><br><span class="line">                        <span class="string">&quot;Request input stream is not of type [&quot;</span> + paramType.getName() + <span class="string">&quot;]: &quot;</span> + inputStream);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">return</span> inputStream;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> (Reader.class.isAssignableFrom(paramType)) &#123;</span><br><span class="line">            <span class="type">Reader</span> <span class="variable">reader</span> <span class="operator">=</span> request.getReader();</span><br><span class="line">            <span class="keyword">if</span> (reader != <span class="literal">null</span> &amp;&amp; !paramType.isInstance(reader)) &#123;</span><br><span class="line">                <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalStateException</span>(</span><br><span class="line">                        <span class="string">&quot;Request body reader is not of type [&quot;</span> + paramType.getName() + <span class="string">&quot;]: &quot;</span> + reader);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">return</span> reader;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> (Principal.class.isAssignableFrom(paramType)) &#123;</span><br><span class="line">            <span class="type">Principal</span> <span class="variable">userPrincipal</span> <span class="operator">=</span> request.getUserPrincipal();</span><br><span class="line">            <span class="keyword">if</span> (userPrincipal != <span class="literal">null</span> &amp;&amp; !paramType.isInstance(userPrincipal)) &#123;</span><br><span class="line">                <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalStateException</span>(</span><br><span class="line">                        <span class="string">&quot;Current user principal is not of type [&quot;</span> + paramType.getName() + <span class="string">&quot;]: &quot;</span> + userPrincipal);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">return</span> userPrincipal;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> (HttpMethod.class == paramType) &#123;</span><br><span class="line">            <span class="keyword">return</span> HttpMethod.resolve(request.getMethod());</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> (Locale.class == paramType) &#123;</span><br><span class="line">            <span class="keyword">return</span> RequestContextUtils.getLocale(request);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> (TimeZone.class == paramType) &#123;</span><br><span class="line">            <span class="type">TimeZone</span> <span class="variable">timeZone</span> <span class="operator">=</span> RequestContextUtils.getTimeZone(request);</span><br><span class="line">            <span class="keyword">return</span> (timeZone != <span class="literal">null</span> ? timeZone : TimeZone.getDefault());</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> (ZoneId.class == paramType) &#123;</span><br><span class="line">            <span class="type">TimeZone</span> <span class="variable">timeZone</span> <span class="operator">=</span> RequestContextUtils.getTimeZone(request);</span><br><span class="line">            <span class="keyword">return</span> (timeZone != <span class="literal">null</span> ? timeZone.toZoneId() : ZoneId.systemDefault());</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// Should never happen...</span></span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">UnsupportedOperationException</span>(<span class="string">&quot;Unknown parameter type: &quot;</span> + paramType.getName());</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Inner class to avoid a hard dependency on Servlet API 4.0 at runtime.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">PushBuilderDelegate</span> &#123;</span><br><span class="line"></span><br><span class="line">        <span class="meta">@Nullable</span></span><br><span class="line">        <span class="keyword">public</span> <span class="keyword">static</span> Object <span class="title function_">resolvePushBuilder</span><span class="params">(HttpServletRequest request, Class&lt;?&gt; paramType)</span> &#123;</span><br><span class="line">            <span class="type">PushBuilder</span> <span class="variable">pushBuilder</span> <span class="operator">=</span> request.newPushBuilder();</span><br><span class="line">            <span class="keyword">if</span> (pushBuilder != <span class="literal">null</span> &amp;&amp; !paramType.isInstance(pushBuilder)) &#123;</span><br><span class="line">                <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalStateException</span>(</span><br><span class="line">                        <span class="string">&quot;Current push builder is not of type [&quot;</span> + paramType.getName() + <span class="string">&quot;]: &quot;</span> + pushBuilder);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">return</span> pushBuilder;</span><br><span class="line"></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>用例：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Controller</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">RequestController</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@GetMapping(&quot;/goto&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">goToPage</span><span class="params">(HttpServletRequest request)</span>&#123;</span><br><span class="line"></span><br><span class="line">        request.setAttribute(<span class="string">&quot;msg&quot;</span>,<span class="string">&quot;成功了...&quot;</span>);</span><br><span class="line">        request.setAttribute(<span class="string">&quot;code&quot;</span>,<span class="number">200</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;forward:/success&quot;</span>;  <span class="comment">//转发到  /success请求</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h1 id="Model、Map原理"><a href="#Model、Map原理" class="headerlink" title="Model、Map原理"></a>Model、Map原理</h1><p>复杂参数：</p>
<ul>
<li><p><strong>Map</strong></p>
</li>
<li><p><strong>Model（map、model里面的数据会被放在request的请求域  request.setAttribute）</strong></p>
</li>
<li><p>Errors/BindingResult</p>
</li>
<li><p><strong>RedirectAttributes（ 重定向携带数据）</strong></p>
</li>
<li><p><strong>ServletResponse（response）</strong></p>
</li>
<li><p>SessionStatus</p>
</li>
<li><p>UriComponentsBuilder</p>
</li>
<li><p>ServletUriComponentsBuilder</p>
</li>
</ul>
<p>用例：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@GetMapping(&quot;/params&quot;)</span></span><br><span class="line"><span class="keyword">public</span> String <span class="title function_">testParam</span><span class="params">(Map&lt;String,Object&gt; map,</span></span><br><span class="line"><span class="params">                        Model model,</span></span><br><span class="line"><span class="params">                        HttpServletRequest request,</span></span><br><span class="line"><span class="params">                        HttpServletResponse response)</span>&#123;</span><br><span class="line">    <span class="comment">//下面三位都是可以给request域中放数据</span></span><br><span class="line">    map.put(<span class="string">&quot;hello&quot;</span>,<span class="string">&quot;world666&quot;</span>);</span><br><span class="line">    model.addAttribute(<span class="string">&quot;world&quot;</span>,<span class="string">&quot;hello666&quot;</span>);</span><br><span class="line">    request.setAttribute(<span class="string">&quot;message&quot;</span>,<span class="string">&quot;HelloWorld&quot;</span>);</span><br><span class="line"></span><br><span class="line">    <span class="type">Cookie</span> <span class="variable">cookie</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Cookie</span>(<span class="string">&quot;c1&quot;</span>,<span class="string">&quot;v1&quot;</span>);</span><br><span class="line">    response.addCookie(cookie);</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;forward:/success&quot;</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@ResponseBody</span></span><br><span class="line"><span class="meta">@GetMapping(&quot;/success&quot;)</span></span><br><span class="line"><span class="keyword">public</span> Map <span class="title function_">success</span><span class="params">(<span class="meta">@RequestAttribute(value = &quot;msg&quot;,required = false)</span> String msg,</span></span><br><span class="line"><span class="params">                   <span class="meta">@RequestAttribute(value = &quot;code&quot;,required = false)</span>Integer code,</span></span><br><span class="line"><span class="params">                   HttpServletRequest request)</span>&#123;</span><br><span class="line">    <span class="type">Object</span> <span class="variable">msg1</span> <span class="operator">=</span> request.getAttribute(<span class="string">&quot;msg&quot;</span>);</span><br><span class="line"></span><br><span class="line">    Map&lt;String,Object&gt; map = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;&gt;();</span><br><span class="line">    <span class="type">Object</span> <span class="variable">hello</span> <span class="operator">=</span> request.getAttribute(<span class="string">&quot;hello&quot;</span>);<span class="comment">//得出testParam方法赋予的值 world666</span></span><br><span class="line">    <span class="type">Object</span> <span class="variable">world</span> <span class="operator">=</span> request.getAttribute(<span class="string">&quot;world&quot;</span>);<span class="comment">//得出testParam方法赋予的值 hello666</span></span><br><span class="line">    <span class="type">Object</span> <span class="variable">message</span> <span class="operator">=</span> request.getAttribute(<span class="string">&quot;message&quot;</span>);<span class="comment">//得出testParam方法赋予的值 HelloWorld</span></span><br><span class="line"></span><br><span class="line">    map.put(<span class="string">&quot;reqMethod_msg&quot;</span>,msg1);</span><br><span class="line">    map.put(<span class="string">&quot;annotation_msg&quot;</span>,msg);</span><br><span class="line">    map.put(<span class="string">&quot;hello&quot;</span>,hello);</span><br><span class="line">    map.put(<span class="string">&quot;world&quot;</span>,world);</span><br><span class="line">    map.put(<span class="string">&quot;message&quot;</span>,message);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> map;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<ul>
<li><p><code>Map&lt;String,Object&gt; map</code></p>
</li>
<li><p><code>Model model</code></p>
</li>
<li><p><code>HttpServletRequest request</code> </p>
</li>
</ul>
<p>上面三位都是可以给request域中放数据，用<code>request.getAttribute()</code>获取</p>
<p>接下来我们看看，<code>Map&lt;String,Object&gt; map</code>与<code>Model model</code>用什么参数处理器。</p>
<hr>
<p><code>Map&lt;String,Object&gt; map</code>参数用<code>MapMethodProcessor</code>处理：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MapMethodProcessor</span> <span class="keyword">implements</span> <span class="title class_">HandlerMethodArgumentResolver</span>, HandlerMethodReturnValueHandler &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">supportsParameter</span><span class="params">(MethodParameter parameter)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> (Map.class.isAssignableFrom(parameter.getParameterType()) &amp;&amp;</span><br><span class="line">                parameter.getParameterAnnotations().length == <span class="number">0</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="meta">@Nullable</span></span><br><span class="line">    <span class="keyword">public</span> Object <span class="title function_">resolveArgument</span><span class="params">(MethodParameter parameter, <span class="meta">@Nullable</span> ModelAndViewContainer mavContainer,</span></span><br><span class="line"><span class="params">            NativeWebRequest webRequest, <span class="meta">@Nullable</span> WebDataBinderFactory binderFactory)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">        Assert.state(mavContainer != <span class="literal">null</span>, <span class="string">&quot;ModelAndViewContainer is required for model exposure&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> mavContainer.getModel();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    ...</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p><code>mavContainer.getModel()</code>如下：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ModelAndViewContainer</span> &#123;</span><br><span class="line"></span><br><span class="line">    ...</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> <span class="type">ModelMap</span> <span class="variable">defaultModel</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">BindingAwareModelMap</span>();</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Nullable</span></span><br><span class="line">    <span class="keyword">private</span> ModelMap redirectModel;</span><br><span class="line"></span><br><span class="line">    ...</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> ModelMap <span class="title function_">getModel</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (useDefaultModel()) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="built_in">this</span>.defaultModel;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="keyword">if</span> (<span class="built_in">this</span>.redirectModel == <span class="literal">null</span>) &#123;</span><br><span class="line">                <span class="built_in">this</span>.redirectModel = <span class="keyword">new</span> <span class="title class_">ModelMap</span>();</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">return</span> <span class="built_in">this</span>.redirectModel;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="type">boolean</span> <span class="title function_">useDefaultModel</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> (!<span class="built_in">this</span>.redirectModelScenario || (<span class="built_in">this</span>.redirectModel == <span class="literal">null</span> &amp;&amp; !<span class="built_in">this</span>.ignoreDefaultModelOnRedirect));</span><br><span class="line">    &#125;</span><br><span class="line">    ...</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<hr>
<p><code>Model model</code>用<code>ModelMethodProcessor</code>处理：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ModelMethodProcessor</span> <span class="keyword">implements</span> <span class="title class_">HandlerMethodArgumentResolver</span>, HandlerMethodReturnValueHandler &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">supportsParameter</span><span class="params">(MethodParameter parameter)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> Model.class.isAssignableFrom(parameter.getParameterType());</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="meta">@Nullable</span></span><br><span class="line">    <span class="keyword">public</span> Object <span class="title function_">resolveArgument</span><span class="params">(MethodParameter parameter, <span class="meta">@Nullable</span> ModelAndViewContainer mavContainer,</span></span><br><span class="line"><span class="params">            NativeWebRequest webRequest, <span class="meta">@Nullable</span> WebDataBinderFactory binderFactory)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">        Assert.state(mavContainer != <span class="literal">null</span>, <span class="string">&quot;ModelAndViewContainer is required for model exposure&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> mavContainer.getModel();</span><br><span class="line">    &#125;</span><br><span class="line">    ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p><code>return mavContainer.getModel();</code>这跟<code>MapMethodProcessor</code>的一致</p>
<center>
    <img style="border-radius: 0.3125em;
    box-shadow: 0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.08);" 
    src="https://gcore.jsdelivr.net/gh/shimmerjordan/pic_bed@main/blog/SpringBoot2/image/20210205010247689.png" width="70%">
    <br>
    <div style="color:orange; border-bottom: 1px solid #d9d9d9;
    display: inline-block;
    color: #999;
    padding: 2px;">Model也是另一种意义的Map</div>
</center>

<p><code>Model</code>也是另一种意义的<code>Map</code>。</p>
<hr>
<p><strong>接下来看看</strong><code>Map&lt;String,Object&gt; map</code>与<code>Model model</code>值是如何做到用<code>request.getAttribute()</code>获取的。</p>
<p>众所周知，所有的数据都放在 <strong>ModelAndView</strong>包含要去的页面地址View，还包含Model数据。</p>
<p>先看<strong>ModelAndView</strong>接下来是如何处理的？</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">DispatcherServlet</span> <span class="keyword">extends</span> <span class="title class_">FrameworkServlet</span> &#123;</span><br><span class="line"></span><br><span class="line">    ...</span><br><span class="line"></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">doDispatch</span><span class="params">(HttpServletRequest request, HttpServletResponse response)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        ...</span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="type">ModelAndView</span> <span class="variable">mv</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line"></span><br><span class="line">            ...</span><br><span class="line"></span><br><span class="line">            <span class="comment">// Actually invoke the handler.</span></span><br><span class="line">            mv = ha.handle(processedRequest, response, mappedHandler.getHandler());</span><br><span class="line"></span><br><span class="line">            ...</span><br><span class="line"></span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">catch</span> (Exception ex) &#123;</span><br><span class="line">                dispatchException = ex;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">catch</span> (Throwable err) &#123;</span><br><span class="line">                <span class="comment">// As of 4.3, we&#x27;re processing Errors thrown from handler methods as well,</span></span><br><span class="line">                <span class="comment">// making them available for @ExceptionHandler methods and other scenarios.</span></span><br><span class="line">                dispatchException = <span class="keyword">new</span> <span class="title class_">NestedServletException</span>(<span class="string">&quot;Handler dispatch failed&quot;</span>, err);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="comment">//处理分发结果</span></span><br><span class="line">            processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);</span><br><span class="line">        &#125;</span><br><span class="line">        ...</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">processDispatchResult</span><span class="params">(HttpServletRequest request, HttpServletResponse response,</span></span><br><span class="line"><span class="params">            <span class="meta">@Nullable</span> HandlerExecutionChain mappedHandler, <span class="meta">@Nullable</span> ModelAndView mv,</span></span><br><span class="line"><span class="params">            <span class="meta">@Nullable</span> Exception exception)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        ...</span><br><span class="line"></span><br><span class="line">        <span class="comment">// Did the handler return a view to render?</span></span><br><span class="line">        <span class="keyword">if</span> (mv != <span class="literal">null</span> &amp;&amp; !mv.wasCleared()) &#123;</span><br><span class="line">            render(mv, request, response);</span><br><span class="line">            ...</span><br><span class="line">        &#125;</span><br><span class="line">        ...</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">render</span><span class="params">(ModelAndView mv, HttpServletRequest request, HttpServletResponse response)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        ...</span><br><span class="line"></span><br><span class="line">        View view;</span><br><span class="line">        <span class="type">String</span> <span class="variable">viewName</span> <span class="operator">=</span> mv.getViewName();</span><br><span class="line">        <span class="keyword">if</span> (viewName != <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="comment">// We need to resolve the view name.</span></span><br><span class="line">            view = resolveViewName(viewName, mv.getModelInternal(), locale, request);</span><br><span class="line">            <span class="keyword">if</span> (view == <span class="literal">null</span>) &#123;</span><br><span class="line">                <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">ServletException</span>(<span class="string">&quot;Could not resolve view with name &#x27;&quot;</span> + mv.getViewName() +</span><br><span class="line">                        <span class="string">&quot;&#x27; in servlet with name &#x27;&quot;</span> + getServletName() + <span class="string">&quot;&#x27;&quot;</span>);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="comment">// No need to lookup: the ModelAndView object contains the actual View object.</span></span><br><span class="line">            view = mv.getView();</span><br><span class="line">            <span class="keyword">if</span> (view == <span class="literal">null</span>) &#123;</span><br><span class="line">                <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">ServletException</span>(<span class="string">&quot;ModelAndView [&quot;</span> + mv + <span class="string">&quot;] neither contains a view name nor a &quot;</span> +</span><br><span class="line">                        <span class="string">&quot;View object in servlet with name &#x27;&quot;</span> + getServletName() + <span class="string">&quot;&#x27;&quot;</span>);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        view.render(mv.getModelInternal(), request, response);</span><br><span class="line"></span><br><span class="line">        ...</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>在Debug模式下，<code>view</code>属为<code>InternalResourceView</code>类。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">InternalResourceView</span> <span class="keyword">extends</span> <span class="title class_">AbstractUrlBasedView</span> &#123;</span><br><span class="line"></span><br><span class="line">     <span class="meta">@Override</span><span class="comment">//该方法在AbstractView，AbstractUrlBasedView继承了AbstractView</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">render</span><span class="params">(<span class="meta">@Nullable</span> Map&lt;String, ?&gt; model, HttpServletRequest request,</span></span><br><span class="line"><span class="params">            HttpServletResponse response)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">        ...</span><br><span class="line"></span><br><span class="line">        Map&lt;String, Object&gt; mergedModel = createMergedOutputModel(model, request, response);</span><br><span class="line">        prepareResponse(request, response);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//看下一个方法实现</span></span><br><span class="line">        renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">renderMergedOutputModel</span><span class="params">(</span></span><br><span class="line"><span class="params">            Map&lt;String, Object&gt; model, HttpServletRequest request, HttpServletResponse response)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// Expose the model object as request attributes.</span></span><br><span class="line">        <span class="comment">// 暴露模型作为请求域属性</span></span><br><span class="line">        exposeModelAsRequestAttributes(model, request);<span class="comment">//&lt;---重点</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// Expose helpers as request attributes, if any.</span></span><br><span class="line">        exposeHelpers(request);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// Determine the path for the request dispatcher.</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">dispatcherPath</span> <span class="operator">=</span> prepareForRendering(request, response);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// Obtain a RequestDispatcher for the target resource (typically a JSP).</span></span><br><span class="line">        <span class="type">RequestDispatcher</span> <span class="variable">rd</span> <span class="operator">=</span> getRequestDispatcher(request, dispatcherPath);</span><br><span class="line"></span><br><span class="line">        ...</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//该方法在AbstractView，AbstractUrlBasedView继承了AbstractView</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">exposeModelAsRequestAttributes</span><span class="params">(Map&lt;String, Object&gt; model,</span></span><br><span class="line"><span class="params">            HttpServletRequest request)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">        model.forEach((name, value) -&gt; &#123;</span><br><span class="line">            <span class="keyword">if</span> (value != <span class="literal">null</span>) &#123;</span><br><span class="line">                request.setAttribute(name, value);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">else</span> &#123;</span><br><span class="line">                request.removeAttribute(name);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p><code>exposeModelAsRequestAttributes</code>方法看出，<code>Map&lt;String,Object&gt; map</code>，<code>Model model</code>这两种类型数据可以给request域中放数据，用<code>request.getAttribute()</code>获取。</p>
<h1 id="自定义参数绑定原理"><a href="#自定义参数绑定原理" class="headerlink" title="自定义参数绑定原理"></a>自定义参数绑定原理</h1><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@RestController</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ParameterTestController</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 数据绑定：页面提交的请求数据（GET、POST）都可以和对象属性进行绑定</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> person</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@PostMapping(&quot;/saveuser&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Person <span class="title function_">saveuser</span><span class="params">(Person person)</span>&#123;</span><br><span class="line">        <span class="keyword">return</span> person;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> *     姓名： &lt;input name=&quot;userName&quot;/&gt; &lt;br/&gt;</span></span><br><span class="line"><span class="comment"> *     年龄： &lt;input name=&quot;age&quot;/&gt; &lt;br/&gt;</span></span><br><span class="line"><span class="comment"> *     生日： &lt;input name=&quot;birth&quot;/&gt; &lt;br/&gt;</span></span><br><span class="line"><span class="comment"> *     宠物姓名：&lt;input name=&quot;pet.name&quot;/&gt;&lt;br/&gt;</span></span><br><span class="line"><span class="comment"> *     宠物年龄：&lt;input name=&quot;pet.age&quot;/&gt;</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Data</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Person</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> String userName;</span><br><span class="line">    <span class="keyword">private</span> Integer age;</span><br><span class="line">    <span class="keyword">private</span> Date birth;</span><br><span class="line">    <span class="keyword">private</span> Pet pet;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Data</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Pet</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> String name;</span><br><span class="line">    <span class="keyword">private</span> String age;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>封装过程用到<code>ServletModelAttributeMethodProcessor</code></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ServletModelAttributeMethodProcessor</span> <span class="keyword">extends</span> <span class="title class_">ModelAttributeMethodProcessor</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span><span class="comment">//本方法在ModelAttributeMethodProcessor类，</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">supportsParameter</span><span class="params">(MethodParameter parameter)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> (parameter.hasParameterAnnotation(ModelAttribute.class) ||</span><br><span class="line">                (<span class="built_in">this</span>.annotationNotRequired &amp;&amp; !BeanUtils.isSimpleProperty(parameter.getParameterType())));</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="meta">@Nullable</span><span class="comment">//本方法在ModelAttributeMethodProcessor类，</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">final</span> Object <span class="title function_">resolveArgument</span><span class="params">(MethodParameter parameter, <span class="meta">@Nullable</span> ModelAndViewContainer mavContainer,</span></span><br><span class="line"><span class="params">            NativeWebRequest webRequest, <span class="meta">@Nullable</span> WebDataBinderFactory binderFactory)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">        ...</span><br><span class="line"></span><br><span class="line">        <span class="type">String</span> <span class="variable">name</span> <span class="operator">=</span> ModelFactory.getNameForParameter(parameter);</span><br><span class="line">        <span class="type">ModelAttribute</span> <span class="variable">ann</span> <span class="operator">=</span> parameter.getParameterAnnotation(ModelAttribute.class);</span><br><span class="line">        <span class="keyword">if</span> (ann != <span class="literal">null</span>) &#123;</span><br><span class="line">            mavContainer.setBinding(name, ann.binding());</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="type">Object</span> <span class="variable">attribute</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">        <span class="type">BindingResult</span> <span class="variable">bindingResult</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (mavContainer.containsAttribute(name)) &#123;</span><br><span class="line">            attribute = mavContainer.getModel().get(name);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="comment">// Create attribute instance</span></span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                attribute = createAttribute(name, parameter, binderFactory, webRequest);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">catch</span> (BindException ex) &#123;</span><br><span class="line">                ...</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (bindingResult == <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="comment">// Bean property binding and validation;</span></span><br><span class="line">            <span class="comment">// skipped in case of binding failure on construction.</span></span><br><span class="line">            <span class="type">WebDataBinder</span> <span class="variable">binder</span> <span class="operator">=</span> binderFactory.createBinder(webRequest, attribute, name);</span><br><span class="line">            <span class="keyword">if</span> (binder.getTarget() != <span class="literal">null</span>) &#123;</span><br><span class="line">                <span class="keyword">if</span> (!mavContainer.isBindingDisabled(name)) &#123;</span><br><span class="line">                    <span class="comment">//web数据绑定器，将请求参数的值绑定到指定的JavaBean里面**</span></span><br><span class="line">                    bindRequestParameters(binder, webRequest);</span><br><span class="line">                &#125;</span><br><span class="line">                validateIfApplicable(binder, parameter);</span><br><span class="line">                <span class="keyword">if</span> (binder.getBindingResult().hasErrors() &amp;&amp; isBindExceptionRequired(binder, parameter)) &#123;</span><br><span class="line">                    <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">BindException</span>(binder.getBindingResult());</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="comment">// Value type adaptation, also covering java.util.Optional</span></span><br><span class="line">            <span class="keyword">if</span> (!parameter.getParameterType().isInstance(attribute)) &#123;</span><br><span class="line">                attribute = binder.convertIfNecessary(binder.getTarget(), parameter.getParameterType(), parameter);</span><br><span class="line">            &#125;</span><br><span class="line">            bindingResult = binder.getBindingResult();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// Add resolved attribute and BindingResult at the end of the model</span></span><br><span class="line">        Map&lt;String, Object&gt; bindingResultModel = bindingResult.getModel();</span><br><span class="line">        mavContainer.removeAttributes(bindingResultModel);</span><br><span class="line">        mavContainer.addAllAttributes(bindingResultModel);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> attribute;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p><strong>WebDataBinder 利用它里面的 Converters 将请求数据转成指定的数据类型。再次封装到JavaBean中</strong></p>
<p><strong>在过程当中，用到GenericConversionService：在设置每一个值的时候，找它里面的所有converter那个可以将这个数据类型（request带来参数的字符串）转换到指定的类型</strong></p>
<h1 id="自定义Converter原理"><a href="#自定义Converter原理" class="headerlink" title="自定义Converter原理"></a>自定义Converter原理</h1><p>未来我们可以给WebDataBinder里面放自己的Converter；</p>
<p>下面演示将字符串<code>“啊猫,3”</code>转换成<code>Pet</code>对象。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//1、WebMvcConfigurer定制化SpringMVC的功能</span></span><br><span class="line"><span class="meta">@Bean</span></span><br><span class="line"><span class="keyword">public</span> WebMvcConfigurer <span class="title function_">webMvcConfigurer</span><span class="params">()</span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">WebMvcConfigurer</span>() &#123;</span><br><span class="line"></span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">addFormatters</span><span class="params">(FormatterRegistry registry)</span> &#123;</span><br><span class="line">            registry.addConverter(<span class="keyword">new</span> <span class="title class_">Converter</span>&lt;String, Pet&gt;() &#123;</span><br><span class="line"></span><br><span class="line">                <span class="meta">@Override</span></span><br><span class="line">                <span class="keyword">public</span> Pet <span class="title function_">convert</span><span class="params">(String source)</span> &#123;</span><br><span class="line">                    <span class="comment">// 啊猫,3</span></span><br><span class="line">                    <span class="keyword">if</span>(!StringUtils.isEmpty(source))&#123;</span><br><span class="line">                        <span class="type">Pet</span> <span class="variable">pet</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Pet</span>();</span><br><span class="line">                        String[] split = source.split(<span class="string">&quot;,&quot;</span>);</span><br><span class="line">                        pet.setName(split[<span class="number">0</span>]);</span><br><span class="line">                        pet.setAge(Integer.parseInt(split[<span class="number">1</span>]));</span><br><span class="line">                        <span class="keyword">return</span> pet;</span><br><span class="line">                    &#125;</span><br><span class="line">                    <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h1 id="ReturnValueHandler原理"><a href="#ReturnValueHandler原理" class="headerlink" title="ReturnValueHandler原理"></a>ReturnValueHandler原理</h1><center>
    <img style="border-radius: 0.3125em;
    box-shadow: 0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.08);" 
    src="https://gcore.jsdelivr.net/gh/shimmerjordan/pic_bed@main/blog/SpringBoot2/image/20210205010403920.jpg" width="70%">
    <br>
    <div style="color:orange; border-bottom: 1px solid #d9d9d9;
    display: inline-block;
    color: #999;
    padding: 2px;">ReturnValueHandler原理</div>
</center>

<p>假设给前端自动返回json数据，需要引入相关的依赖</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-web<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- web场景自动引入了json场景 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-json<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>2.3.4.RELEASE<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">scope</span>&gt;</span>compile<span class="tag">&lt;/<span class="name">scope</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure>
<p>控制层代码如下：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Controller</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ResponseTestController</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@ResponseBody</span>  <span class="comment">//利用返回值处理器里面的消息转换器进行处理</span></span><br><span class="line">    <span class="meta">@GetMapping(value = &quot;/test/person&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Person <span class="title function_">getPerson</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="type">Person</span> <span class="variable">person</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Person</span>();</span><br><span class="line">        person.setAge(<span class="number">28</span>);</span><br><span class="line">        person.setBirth(<span class="keyword">new</span> <span class="title class_">Date</span>());</span><br><span class="line">        person.setUserName(<span class="string">&quot;zhangsan&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> person;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p><a href="#">32、请求处理-【源码分析】-各种类型参数解析原理 - 返回值处理器</a>有讨论<strong>ReturnValueHandler</strong>。现在直接看看重点：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">RequestMappingHandlerAdapter</span> <span class="keyword">extends</span> <span class="title class_">AbstractHandlerMethodAdapter</span></span><br><span class="line">        <span class="keyword">implements</span> <span class="title class_">BeanFactoryAware</span>, InitializingBean &#123;</span><br><span class="line"></span><br><span class="line">    ...</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Nullable</span></span><br><span class="line">    <span class="keyword">protected</span> ModelAndView <span class="title function_">invokeHandlerMethod</span><span class="params">(HttpServletRequest request,</span></span><br><span class="line"><span class="params">            HttpServletResponse response, HandlerMethod handlerMethod)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">        <span class="type">ServletWebRequest</span> <span class="variable">webRequest</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ServletWebRequest</span>(request, response);</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line"></span><br><span class="line">            ...</span><br><span class="line"></span><br><span class="line">            <span class="type">ServletInvocableHandlerMethod</span> <span class="variable">invocableMethod</span> <span class="operator">=</span> createInvocableHandlerMethod(handlerMethod);</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span> (<span class="built_in">this</span>.argumentResolvers != <span class="literal">null</span>) &#123;</span><br><span class="line">                invocableMethod.setHandlerMethodArgumentResolvers(<span class="built_in">this</span>.argumentResolvers);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (<span class="built_in">this</span>.returnValueHandlers != <span class="literal">null</span>) &#123;<span class="comment">//&lt;----关注点</span></span><br><span class="line">                invocableMethod.setHandlerMethodReturnValueHandlers(<span class="built_in">this</span>.returnValueHandlers);</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            ...</span><br><span class="line"></span><br><span class="line">            invocableMethod.invokeAndHandle(webRequest, mavContainer);<span class="comment">//看下块代码</span></span><br><span class="line">            <span class="keyword">if</span> (asyncManager.isConcurrentHandlingStarted()) &#123;</span><br><span class="line">                <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">return</span> getModelAndView(mavContainer, modelFactory, webRequest);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">finally</span> &#123;</span><br><span class="line">            webRequest.requestCompleted();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ServletInvocableHandlerMethod</span> <span class="keyword">extends</span> <span class="title class_">InvocableHandlerMethod</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">invokeAndHandle</span><span class="params">(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,</span></span><br><span class="line"><span class="params">            Object... providedArgs)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">        <span class="type">Object</span> <span class="variable">returnValue</span> <span class="operator">=</span> invokeForRequest(webRequest, mavContainer, providedArgs);</span><br><span class="line"></span><br><span class="line">        ...</span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">//看下块代码</span></span><br><span class="line">            <span class="built_in">this</span>.returnValueHandlers.handleReturnValue(</span><br><span class="line">                    returnValue, getReturnValueType(returnValue), mavContainer, webRequest);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">catch</span> (Exception ex) &#123;</span><br><span class="line">            ...</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">HandlerMethodReturnValueHandlerComposite</span> <span class="keyword">implements</span> <span class="title class_">HandlerMethodReturnValueHandler</span> &#123;</span><br><span class="line"></span><br><span class="line">    ...</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">handleReturnValue</span><span class="params">(<span class="meta">@Nullable</span> Object returnValue, MethodParameter returnType,</span></span><br><span class="line"><span class="params">            ModelAndViewContainer mavContainer, NativeWebRequest webRequest)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//selectHandler()实现在下面</span></span><br><span class="line">        <span class="type">HandlerMethodReturnValueHandler</span> <span class="variable">handler</span> <span class="operator">=</span> selectHandler(returnValue, returnType);</span><br><span class="line">        <span class="keyword">if</span> (handler == <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalArgumentException</span>(<span class="string">&quot;Unknown return value type: &quot;</span> + returnType.getParameterType().getName());</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//开始处理</span></span><br><span class="line">        handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">       <span class="meta">@Nullable</span></span><br><span class="line">    <span class="keyword">private</span> HandlerMethodReturnValueHandler <span class="title function_">selectHandler</span><span class="params">(<span class="meta">@Nullable</span> Object value, MethodParameter returnType)</span> &#123;</span><br><span class="line">        <span class="type">boolean</span> <span class="variable">isAsyncValue</span> <span class="operator">=</span> isAsyncReturnValue(value, returnType);</span><br><span class="line">        <span class="keyword">for</span> (HandlerMethodReturnValueHandler handler : <span class="built_in">this</span>.returnValueHandlers) &#123;</span><br><span class="line">            <span class="keyword">if</span> (isAsyncValue &amp;&amp; !(handler <span class="keyword">instanceof</span> AsyncHandlerMethodReturnValueHandler)) &#123;</span><br><span class="line">                <span class="keyword">continue</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (handler.supportsReturnType(returnType)) &#123;</span><br><span class="line">                <span class="keyword">return</span> handler;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>
<p><code>@ResponseBody</code> 注解，即<code>RequestResponseBodyMethodProcessor</code>，它实现<code>HandlerMethodReturnValueHandler</code>接口</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">RequestResponseBodyMethodProcessor</span> <span class="keyword">extends</span> <span class="title class_">AbstractMessageConverterMethodProcessor</span> &#123;</span><br><span class="line"></span><br><span class="line">    ...</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">handleReturnValue</span><span class="params">(<span class="meta">@Nullable</span> Object returnValue, MethodParameter returnType,</span></span><br><span class="line"><span class="params">            ModelAndViewContainer mavContainer, NativeWebRequest webRequest)</span></span><br><span class="line">            <span class="keyword">throws</span> IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException &#123;</span><br><span class="line"></span><br><span class="line">        mavContainer.setRequestHandled(<span class="literal">true</span>);</span><br><span class="line">        <span class="type">ServletServerHttpRequest</span> <span class="variable">inputMessage</span> <span class="operator">=</span> createInputMessage(webRequest);</span><br><span class="line">        <span class="type">ServletServerHttpResponse</span> <span class="variable">outputMessage</span> <span class="operator">=</span> createOutputMessage(webRequest);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 使用消息转换器进行写出操作，本方法下一章节介绍：</span></span><br><span class="line">        <span class="comment">// Try even with null return value. ResponseBodyAdvice could get involved.</span></span><br><span class="line">        writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h1 id="HTTPMessageConverter原理"><a href="#HTTPMessageConverter原理" class="headerlink" title="HTTPMessageConverter原理"></a>HTTPMessageConverter原理</h1><p>返回值处理器<code>ReturnValueHandler</code>原理：</p>
<ol>
<li>返回值处理器判断是否支持这种类型返回值 <code>supportsReturnType</code></li>
<li>返回值处理器调用 <code>handleReturnValue</code> 进行处理</li>
<li><code>RequestResponseBodyMethodProcessor</code> 可以处理返回值标了<code>@ResponseBody</code> 注解的。<ul>
<li>利用 <code>MessageConverters</code> 进行处理 将数据写为json<ol>
<li>内容协商（浏览器默认会以请求头的方式告诉服务器他能接受什么样的内容类型）</li>
<li>服务器最终根据自己自身的能力，决定服务器能生产出什么样内容类型的数据，</li>
<li>SpringMVC会挨个遍历所有容器底层的 <code>HttpMessageConverter</code> ，看谁能处理？<ol>
<li>得到<code>MappingJackson2HttpMessageConverter</code>可以将对象写为json</li>
<li>利用<code>MappingJackson2HttpMessageConverter</code>将对象转为json再写出去。</li>
</ol>
</li>
</ol>
</li>
</ul>
</li>
</ol>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//RequestResponseBodyMethodProcessor继承这类</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">class</span> <span class="title class_">AbstractMessageConverterMethodProcessor</span> <span class="keyword">extends</span> <span class="title class_">AbstractMessageConverterMethodArgumentResolver</span></span><br><span class="line">        <span class="keyword">implements</span> <span class="title class_">HandlerMethodReturnValueHandler</span> &#123;</span><br><span class="line"></span><br><span class="line">    ...</span><br><span class="line"></span><br><span class="line">    <span class="comment">//承接上一节内容</span></span><br><span class="line">    <span class="keyword">protected</span> &lt;T&gt; <span class="keyword">void</span> <span class="title function_">writeWithMessageConverters</span><span class="params">(<span class="meta">@Nullable</span> T value, MethodParameter returnType,</span></span><br><span class="line"><span class="params">                ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)</span></span><br><span class="line">                <span class="keyword">throws</span> IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException &#123;</span><br><span class="line"></span><br><span class="line">            Object body;</span><br><span class="line">            Class&lt;?&gt; valueType;</span><br><span class="line">            Type targetType;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span> (value <span class="keyword">instanceof</span> CharSequence) &#123;</span><br><span class="line">                body = value.toString();</span><br><span class="line">                valueType = String.class;</span><br><span class="line">                targetType = String.class;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">else</span> &#123;</span><br><span class="line">                body = value;</span><br><span class="line">                valueType = getReturnValueType(body, returnType);</span><br><span class="line">                targetType = GenericTypeResolver.resolveType(getGenericType(returnType), returnType.getContainingClass());</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            ...</span><br><span class="line"></span><br><span class="line">            <span class="comment">//内容协商（浏览器默认会以请求头(参数Accept)的方式告诉服务器他能接受什么样的内容类型）</span></span><br><span class="line">            <span class="type">MediaType</span> <span class="variable">selectedMediaType</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">            <span class="type">MediaType</span> <span class="variable">contentType</span> <span class="operator">=</span> outputMessage.getHeaders().getContentType();</span><br><span class="line">            <span class="type">boolean</span> <span class="variable">isContentTypePreset</span> <span class="operator">=</span> contentType != <span class="literal">null</span> &amp;&amp; contentType.isConcrete();</span><br><span class="line">            <span class="keyword">if</span> (isContentTypePreset) &#123;</span><br><span class="line">                <span class="keyword">if</span> (logger.isDebugEnabled()) &#123;</span><br><span class="line">                    logger.debug(<span class="string">&quot;Found &#x27;Content-Type:&quot;</span> + contentType + <span class="string">&quot;&#x27; in response&quot;</span>);</span><br><span class="line">                &#125;</span><br><span class="line">                selectedMediaType = contentType;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="type">HttpServletRequest</span> <span class="variable">request</span> <span class="operator">=</span> inputMessage.getServletRequest();</span><br><span class="line">                List&lt;MediaType&gt; acceptableTypes = getAcceptableMediaTypes(request);</span><br><span class="line">                <span class="comment">//服务器最终根据自己自身的能力，决定服务器能生产出什么样内容类型的数据</span></span><br><span class="line">                List&lt;MediaType&gt; producibleTypes = getProducibleMediaTypes(request, valueType, targetType);</span><br><span class="line"></span><br><span class="line">                <span class="keyword">if</span> (body != <span class="literal">null</span> &amp;&amp; producibleTypes.isEmpty()) &#123;</span><br><span class="line">                    <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">HttpMessageNotWritableException</span>(</span><br><span class="line">                            <span class="string">&quot;No converter found for return value of type: &quot;</span> + valueType);</span><br><span class="line">                &#125;</span><br><span class="line">                List&lt;MediaType&gt; mediaTypesToUse = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">                <span class="keyword">for</span> (MediaType requestedType : acceptableTypes) &#123;</span><br><span class="line">                    <span class="keyword">for</span> (MediaType producibleType : producibleTypes) &#123;</span><br><span class="line">                        <span class="keyword">if</span> (requestedType.isCompatibleWith(producibleType)) &#123;</span><br><span class="line">                            mediaTypesToUse.add(getMostSpecificMediaType(requestedType, producibleType));</span><br><span class="line">                        &#125;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">if</span> (mediaTypesToUse.isEmpty()) &#123;</span><br><span class="line">                    <span class="keyword">if</span> (body != <span class="literal">null</span>) &#123;</span><br><span class="line">                        <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">HttpMediaTypeNotAcceptableException</span>(producibleTypes);</span><br><span class="line">                    &#125;</span><br><span class="line">                    <span class="keyword">if</span> (logger.isDebugEnabled()) &#123;</span><br><span class="line">                        logger.debug(<span class="string">&quot;No match for &quot;</span> + acceptableTypes + <span class="string">&quot;, supported: &quot;</span> + producibleTypes);</span><br><span class="line">                    &#125;</span><br><span class="line">                    <span class="keyword">return</span>;</span><br><span class="line">                &#125;</span><br><span class="line"></span><br><span class="line">                MediaType.sortBySpecificityAndQuality(mediaTypesToUse);</span><br><span class="line"></span><br><span class="line">                <span class="comment">//选择一个MediaType</span></span><br><span class="line">                <span class="keyword">for</span> (MediaType mediaType : mediaTypesToUse) &#123;</span><br><span class="line">                    <span class="keyword">if</span> (mediaType.isConcrete()) &#123;</span><br><span class="line">                        selectedMediaType = mediaType;</span><br><span class="line">                        <span class="keyword">break</span>;</span><br><span class="line">                    &#125;</span><br><span class="line">                    <span class="keyword">else</span> <span class="keyword">if</span> (mediaType.isPresentIn(ALL_APPLICATION_MEDIA_TYPES)) &#123;</span><br><span class="line">                        selectedMediaType = MediaType.APPLICATION_OCTET_STREAM;</span><br><span class="line">                        <span class="keyword">break</span>;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line"></span><br><span class="line">                <span class="keyword">if</span> (logger.isDebugEnabled()) &#123;</span><br><span class="line">                    logger.debug(<span class="string">&quot;Using &#x27;&quot;</span> + selectedMediaType + <span class="string">&quot;&#x27;, given &quot;</span> +</span><br><span class="line">                            acceptableTypes + <span class="string">&quot; and supported &quot;</span> + producibleTypes);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span> (selectedMediaType != <span class="literal">null</span>) &#123;</span><br><span class="line">                selectedMediaType = selectedMediaType.removeQualityValue();</span><br><span class="line">                <span class="comment">//本节主角：HttpMessageConverter</span></span><br><span class="line">                <span class="keyword">for</span> (HttpMessageConverter&lt;?&gt; converter : <span class="built_in">this</span>.messageConverters) &#123;</span><br><span class="line">                    <span class="type">GenericHttpMessageConverter</span> <span class="variable">genericConverter</span> <span class="operator">=</span> (converter <span class="keyword">instanceof</span> GenericHttpMessageConverter ?</span><br><span class="line">                            (GenericHttpMessageConverter&lt;?&gt;) converter : <span class="literal">null</span>);</span><br><span class="line"></span><br><span class="line">                    <span class="comment">//判断是否可写</span></span><br><span class="line">                    <span class="keyword">if</span> (genericConverter != <span class="literal">null</span> ?</span><br><span class="line">                            ((GenericHttpMessageConverter) converter).canWrite(targetType, valueType, selectedMediaType) :</span><br><span class="line">                            converter.canWrite(valueType, selectedMediaType)) &#123;</span><br><span class="line">                        body = getAdvice().beforeBodyWrite(body, returnType, selectedMediaType,</span><br><span class="line">                                (Class&lt;? <span class="keyword">extends</span> <span class="title class_">HttpMessageConverter</span>&lt;?&gt;&gt;) converter.getClass(),</span><br><span class="line">                                inputMessage, outputMessage);</span><br><span class="line">                        <span class="keyword">if</span> (body != <span class="literal">null</span>) &#123;</span><br><span class="line">                            <span class="type">Object</span> <span class="variable">theBody</span> <span class="operator">=</span> body;</span><br><span class="line">                            LogFormatUtils.traceDebug(logger, traceOn -&gt;</span><br><span class="line">                                    <span class="string">&quot;Writing [&quot;</span> + LogFormatUtils.formatValue(theBody, !traceOn) + <span class="string">&quot;]&quot;</span>);</span><br><span class="line">                            addContentDispositionHeader(inputMessage, outputMessage);</span><br><span class="line">                            <span class="comment">//开始写入</span></span><br><span class="line">                            <span class="keyword">if</span> (genericConverter != <span class="literal">null</span>) &#123;</span><br><span class="line">                                genericConverter.write(body, targetType, selectedMediaType, outputMessage);</span><br><span class="line">                            &#125;</span><br><span class="line">                            <span class="keyword">else</span> &#123;</span><br><span class="line">                                ((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage);</span><br><span class="line">                            &#125;</span><br><span class="line">                        &#125;</span><br><span class="line">                        <span class="keyword">else</span> &#123;</span><br><span class="line">                            <span class="keyword">if</span> (logger.isDebugEnabled()) &#123;</span><br><span class="line">                                logger.debug(<span class="string">&quot;Nothing to write: null body&quot;</span>);</span><br><span class="line">                            &#125;</span><br><span class="line">                        &#125;</span><br><span class="line">                        <span class="keyword">return</span>;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            ...</span><br><span class="line">        &#125;</span><br></pre></td></tr></table></figure>
<p><code>HTTPMessageConverter</code>接口：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Strategy interface for converting from and to HTTP requests and responses.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">HttpMessageConverter</span>&lt;T&gt; &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Indicates whether the given class can be read by this converter.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="type">boolean</span> <span class="title function_">canRead</span><span class="params">(Class&lt;?&gt; clazz, <span class="meta">@Nullable</span> MediaType mediaType)</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Indicates whether the given class can be written by this converter.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="type">boolean</span> <span class="title function_">canWrite</span><span class="params">(Class&lt;?&gt; clazz, <span class="meta">@Nullable</span> MediaType mediaType)</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Return the list of &#123;<span class="doctag">@link</span> MediaType&#125; objects supported by this converter.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    List&lt;MediaType&gt; <span class="title function_">getSupportedMediaTypes</span><span class="params">()</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Read an object of the given type from the given input message, and returns it.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    T <span class="title function_">read</span><span class="params">(Class&lt;? extends T&gt; clazz, HttpInputMessage inputMessage)</span></span><br><span class="line">            <span class="keyword">throws</span> IOException, HttpMessageNotReadableException;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Write an given object to the given output message.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">write</span><span class="params">(T t, <span class="meta">@Nullable</span> MediaType contentType, HttpOutputMessage outputMessage)</span></span><br><span class="line">            <span class="keyword">throws</span> IOException, HttpMessageNotWritableException;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p><code>HttpMessageConverter</code>: 看是否支持将 此 <code>Class</code>类型的对象，转为<code>MediaType</code>类型的数据。</p>
<p>例子：<code>Person</code>对象转为JSON，或者 JSON转为<code>Person</code>，这将用到<code>MappingJackson2HttpMessageConverter</code></p>
<center>
    <img style="border-radius: 0.3125em;
    box-shadow: 0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.08);" 
    src="https://gcore.jsdelivr.net/gh/shimmerjordan/pic_bed@main/blog/SpringBoot2/image/20210205010509984.png" width="70%">
    <br>
    <div style="color:orange; border-bottom: 1px solid #d9d9d9;
    display: inline-block;
    color: #999;
    padding: 2px;">MappingJackson2HttpMessageConverter</div>
</center>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MappingJackson2HttpMessageConverter</span> <span class="keyword">extends</span> <span class="title class_">AbstractJackson2HttpMessageConverter</span> &#123;</span><br><span class="line">    ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>关于<code>MappingJackson2HttpMessageConverter</code>的实例化请看下节。</p>
<h3 id="关于HttpMessageConverters的初始化"><a href="#关于HttpMessageConverters的初始化" class="headerlink" title="关于HttpMessageConverters的初始化"></a>关于HttpMessageConverters的初始化</h3><p><code>DispatcherServlet</code>的初始化时会调用<code>initHandlerAdapters(ApplicationContext context)</code></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">DispatcherServlet</span> <span class="keyword">extends</span> <span class="title class_">FrameworkServlet</span> &#123;</span><br><span class="line"></span><br><span class="line">    ...</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">initHandlerAdapters</span><span class="params">(ApplicationContext context)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.handlerAdapters = <span class="literal">null</span>;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (<span class="built_in">this</span>.detectAllHandlerAdapters) &#123;</span><br><span class="line">            <span class="comment">// Find all HandlerAdapters in the ApplicationContext, including ancestor contexts.</span></span><br><span class="line">            Map&lt;String, HandlerAdapter&gt; matchingBeans =</span><br><span class="line">                    BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, <span class="literal">true</span>, <span class="literal">false</span>);</span><br><span class="line">            <span class="keyword">if</span> (!matchingBeans.isEmpty()) &#123;</span><br><span class="line">                <span class="built_in">this</span>.handlerAdapters = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;(matchingBeans.values());</span><br><span class="line">                <span class="comment">// We keep HandlerAdapters in sorted order.</span></span><br><span class="line">                AnnotationAwareOrderComparator.sort(<span class="built_in">this</span>.handlerAdapters);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">      ...</span><br></pre></td></tr></table></figure>
<p>上述代码会加载<code>ApplicationContext</code>的所有<code>HandlerAdapter</code>，用来处理<code>@RequestMapping</code>的<code>RequestMappingHandlerAdapter</code>实现<code>HandlerAdapter</code>接口，<code>RequestMappingHandlerAdapter</code>也被实例化。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">RequestMappingHandlerAdapter</span> <span class="keyword">extends</span> <span class="title class_">AbstractHandlerMethodAdapter</span></span><br><span class="line">        <span class="keyword">implements</span> <span class="title class_">BeanFactoryAware</span>, InitializingBean &#123;</span><br><span class="line"></span><br><span class="line">    ...</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> List&lt;HttpMessageConverter&lt;?&gt;&gt; messageConverters;</span><br><span class="line"></span><br><span class="line">    ...</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">RequestMappingHandlerAdapter</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.messageConverters = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;(<span class="number">4</span>);</span><br><span class="line">        <span class="built_in">this</span>.messageConverters.add(<span class="keyword">new</span> <span class="title class_">ByteArrayHttpMessageConverter</span>());</span><br><span class="line">        <span class="built_in">this</span>.messageConverters.add(<span class="keyword">new</span> <span class="title class_">StringHttpMessageConverter</span>());</span><br><span class="line">        <span class="keyword">if</span> (!shouldIgnoreXml) &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="built_in">this</span>.messageConverters.add(<span class="keyword">new</span> <span class="title class_">SourceHttpMessageConverter</span>&lt;&gt;());</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">catch</span> (Error err) &#123;</span><br><span class="line">                <span class="comment">// Ignore when no TransformerFactory implementation is available</span></span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="built_in">this</span>.messageConverters.add(<span class="keyword">new</span> <span class="title class_">AllEncompassingFormHttpMessageConverter</span>());</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>
<p>在构造器中看到<strong>一堆</strong><code>HttpMessageConverter</code>。接着，重点查看<code>AllEncompassingFormHttpMessageConverter</code>类：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">AllEncompassingFormHttpMessageConverter</span> <span class="keyword">extends</span> <span class="title class_">FormHttpMessageConverter</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Boolean flag controlled by a &#123;<span class="doctag">@code</span> spring.xml.ignore&#125; system property that instructs Spring to</span></span><br><span class="line"><span class="comment">     * ignore XML, i.e. to not initialize the XML-related infrastructure.</span></span><br><span class="line"><span class="comment">     * &lt;p&gt;The default is &quot;false&quot;.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">boolean</span> <span class="variable">shouldIgnoreXml</span> <span class="operator">=</span> SpringProperties.getFlag(<span class="string">&quot;spring.xml.ignore&quot;</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">boolean</span> jaxb2Present;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">boolean</span> jackson2Present;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">boolean</span> jackson2XmlPresent;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">boolean</span> jackson2SmilePresent;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">boolean</span> gsonPresent;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">boolean</span> jsonbPresent;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">boolean</span> kotlinSerializationJsonPresent;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> &#123;</span><br><span class="line">        <span class="type">ClassLoader</span> <span class="variable">classLoader</span> <span class="operator">=</span> AllEncompassingFormHttpMessageConverter.class.getClassLoader();</span><br><span class="line">        jaxb2Present = ClassUtils.isPresent(<span class="string">&quot;javax.xml.bind.Binder&quot;</span>, classLoader);</span><br><span class="line">        jackson2Present = ClassUtils.isPresent(<span class="string">&quot;com.fasterxml.jackson.databind.ObjectMapper&quot;</span>, classLoader) &amp;&amp;</span><br><span class="line">                        ClassUtils.isPresent(<span class="string">&quot;com.fasterxml.jackson.core.JsonGenerator&quot;</span>, classLoader);</span><br><span class="line">        jackson2XmlPresent = ClassUtils.isPresent(<span class="string">&quot;com.fasterxml.jackson.dataformat.xml.XmlMapper&quot;</span>, classLoader);</span><br><span class="line">        jackson2SmilePresent = ClassUtils.isPresent(<span class="string">&quot;com.fasterxml.jackson.dataformat.smile.SmileFactory&quot;</span>, classLoader);</span><br><span class="line">        gsonPresent = ClassUtils.isPresent(<span class="string">&quot;com.google.gson.Gson&quot;</span>, classLoader);</span><br><span class="line">        jsonbPresent = ClassUtils.isPresent(<span class="string">&quot;javax.json.bind.Jsonb&quot;</span>, classLoader);</span><br><span class="line">        kotlinSerializationJsonPresent = ClassUtils.isPresent(<span class="string">&quot;kotlinx.serialization.json.Json&quot;</span>, classLoader);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">AllEncompassingFormHttpMessageConverter</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (!shouldIgnoreXml) &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                addPartConverter(<span class="keyword">new</span> <span class="title class_">SourceHttpMessageConverter</span>&lt;&gt;());</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">catch</span> (Error err) &#123;</span><br><span class="line">                <span class="comment">// Ignore when no TransformerFactory implementation is available</span></span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span> (jaxb2Present &amp;&amp; !jackson2XmlPresent) &#123;</span><br><span class="line">                addPartConverter(<span class="keyword">new</span> <span class="title class_">Jaxb2RootElementHttpMessageConverter</span>());</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (jackson2Present) &#123;</span><br><span class="line">            addPartConverter(<span class="keyword">new</span> <span class="title class_">MappingJackson2HttpMessageConverter</span>());<span class="comment">//&lt;----重点看这里</span></span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> (gsonPresent) &#123;</span><br><span class="line">            addPartConverter(<span class="keyword">new</span> <span class="title class_">GsonHttpMessageConverter</span>());</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> (jsonbPresent) &#123;</span><br><span class="line">            addPartConverter(<span class="keyword">new</span> <span class="title class_">JsonbHttpMessageConverter</span>());</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> (kotlinSerializationJsonPresent) &#123;</span><br><span class="line">            addPartConverter(<span class="keyword">new</span> <span class="title class_">KotlinSerializationJsonHttpMessageConverter</span>());</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (jackson2XmlPresent &amp;&amp; !shouldIgnoreXml) &#123;</span><br><span class="line">            addPartConverter(<span class="keyword">new</span> <span class="title class_">MappingJackson2XmlHttpMessageConverter</span>());</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (jackson2SmilePresent) &#123;</span><br><span class="line">            addPartConverter(<span class="keyword">new</span> <span class="title class_">MappingJackson2SmileHttpMessageConverter</span>());</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">FormHttpMessageConverter</span> <span class="keyword">implements</span> <span class="title class_">HttpMessageConverter</span>&lt;MultiValueMap&lt;String, ?&gt;&gt; &#123;</span><br><span class="line"></span><br><span class="line">    ...</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> List&lt;HttpMessageConverter&lt;?&gt;&gt; partConverters = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line"></span><br><span class="line">    ...</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">addPartConverter</span><span class="params">(HttpMessageConverter&lt;?&gt; partConverter)</span> &#123;</span><br><span class="line">        Assert.notNull(partConverter, <span class="string">&quot;&#x27;partConverter&#x27; must not be null&quot;</span>);</span><br><span class="line">        <span class="built_in">this</span>.partConverters.add(partConverter);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>在<code>AllEncompassingFormHttpMessageConverter</code>类构造器看到<code>MappingJackson2HttpMessageConverter</code>类的实例化，<code>AllEncompassingFormHttpMessageConverter</code><strong>包含</strong><code>MappingJackson2HttpMessageConverter</code>。</p>
<p> <code>ReturnValueHandler</code>是怎么与<code>MappingJackson2HttpMessageConverter</code>关联起来？请看下节。</p>
<h2 id="ReturnValueHandler与MappingJackson2HttpMessageConverter关联"><a href="#ReturnValueHandler与MappingJackson2HttpMessageConverter关联" class="headerlink" title="ReturnValueHandler与MappingJackson2HttpMessageConverter关联"></a>ReturnValueHandler与MappingJackson2HttpMessageConverter关联</h2><p>再次回顾<code>RequestMappingHandlerAdapter</code></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">RequestMappingHandlerAdapter</span> <span class="keyword">extends</span> <span class="title class_">AbstractHandlerMethodAdapter</span></span><br><span class="line">        <span class="keyword">implements</span> <span class="title class_">BeanFactoryAware</span>, InitializingBean &#123;</span><br><span class="line"></span><br><span class="line">    ...</span><br><span class="line">    <span class="meta">@Nullable</span></span><br><span class="line">    <span class="keyword">private</span> HandlerMethodReturnValueHandlerComposite returnValueHandlers;<span class="comment">//我们关注的returnValueHandlers</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="meta">@Nullable</span><span class="comment">//本方法在AbstractHandlerMethodAdapter</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">final</span> ModelAndView <span class="title function_">handle</span><span class="params">(HttpServletRequest request, HttpServletResponse response, Object handler)</span></span><br><span class="line">            <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> handleInternal(request, response, (HandlerMethod) handler);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> ModelAndView <span class="title function_">handleInternal</span><span class="params">(HttpServletRequest request,</span></span><br><span class="line"><span class="params">            HttpServletResponse response, HandlerMethod handlerMethod)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        ModelAndView mav;</span><br><span class="line">        ...</span><br><span class="line">        mav = invokeHandlerMethod(request, response, handlerMethod);</span><br><span class="line">        ...</span><br><span class="line">        <span class="keyword">return</span> mav;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Nullable</span></span><br><span class="line">    <span class="keyword">protected</span> ModelAndView <span class="title function_">invokeHandlerMethod</span><span class="params">(HttpServletRequest request,</span></span><br><span class="line"><span class="params">            HttpServletResponse response, HandlerMethod handlerMethod)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">        <span class="type">ServletWebRequest</span> <span class="variable">webRequest</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ServletWebRequest</span>(request, response);</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="type">WebDataBinderFactory</span> <span class="variable">binderFactory</span> <span class="operator">=</span> getDataBinderFactory(handlerMethod);</span><br><span class="line">            <span class="type">ModelFactory</span> <span class="variable">modelFactory</span> <span class="operator">=</span> getModelFactory(handlerMethod, binderFactory);</span><br><span class="line"></span><br><span class="line">            <span class="type">ServletInvocableHandlerMethod</span> <span class="variable">invocableMethod</span> <span class="operator">=</span> createInvocableHandlerMethod(handlerMethod);</span><br><span class="line">            <span class="keyword">if</span> (<span class="built_in">this</span>.argumentResolvers != <span class="literal">null</span>) &#123;</span><br><span class="line">                invocableMethod.setHandlerMethodArgumentResolvers(<span class="built_in">this</span>.argumentResolvers);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (<span class="built_in">this</span>.returnValueHandlers != <span class="literal">null</span>) &#123;<span class="comment">//&lt;---我们关注的returnValueHandlers</span></span><br><span class="line">                invocableMethod.setHandlerMethodReturnValueHandlers(<span class="built_in">this</span>.returnValueHandlers);</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            ...</span><br><span class="line"></span><br><span class="line">            invocableMethod.invokeAndHandle(webRequest, mavContainer);</span><br><span class="line">            <span class="keyword">if</span> (asyncManager.isConcurrentHandlingStarted()) &#123;</span><br><span class="line">                <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">return</span> getModelAndView(mavContainer, modelFactory, webRequest);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">finally</span> &#123;</span><br><span class="line">            webRequest.requestCompleted();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">   <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">afterPropertiesSet</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="comment">// Do this first, it may add ResponseBody advice beans</span></span><br><span class="line"></span><br><span class="line">        ...</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (<span class="built_in">this</span>.returnValueHandlers == <span class="literal">null</span>) &#123;<span class="comment">//赋值returnValueHandlers</span></span><br><span class="line">            List&lt;HandlerMethodReturnValueHandler&gt; handlers = getDefaultReturnValueHandlers();</span><br><span class="line">            <span class="built_in">this</span>.returnValueHandlers = <span class="keyword">new</span> <span class="title class_">HandlerMethodReturnValueHandlerComposite</span>().addHandlers(handlers);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> List&lt;HandlerMethodReturnValueHandler&gt; <span class="title function_">getDefaultReturnValueHandlers</span><span class="params">()</span> &#123;</span><br><span class="line">        List&lt;HandlerMethodReturnValueHandler&gt; handlers = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;(<span class="number">20</span>);</span><br><span class="line"></span><br><span class="line">        ...</span><br><span class="line">        <span class="comment">// Annotation-based return value types</span></span><br><span class="line">        <span class="comment">//这里就是 ReturnValueHandler与 MappingJackson2HttpMessageConverter关联 的关键点</span></span><br><span class="line">        handlers.add(<span class="keyword">new</span> <span class="title class_">RequestResponseBodyMethodProcessor</span>(getMessageConverters(),<span class="comment">//&lt;---MessageConverters也就传参传进来的</span></span><br><span class="line">                <span class="built_in">this</span>.contentNegotiationManager, <span class="built_in">this</span>.requestResponseBodyAdvice));<span class="comment">//</span></span><br><span class="line">        ...</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> handlers;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//------</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> List&lt;HttpMessageConverter&lt;?&gt;&gt; getMessageConverters() &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">this</span>.messageConverters;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//RequestMappingHandlerAdapter构造器已初始化部分messageConverters</span></span><br><span class="line">       <span class="keyword">public</span> <span class="title function_">RequestMappingHandlerAdapter</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.messageConverters = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;(<span class="number">4</span>);</span><br><span class="line">        <span class="built_in">this</span>.messageConverters.add(<span class="keyword">new</span> <span class="title class_">ByteArrayHttpMessageConverter</span>());</span><br><span class="line">        <span class="built_in">this</span>.messageConverters.add(<span class="keyword">new</span> <span class="title class_">StringHttpMessageConverter</span>());</span><br><span class="line">        <span class="keyword">if</span> (!shouldIgnoreXml) &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="built_in">this</span>.messageConverters.add(<span class="keyword">new</span> <span class="title class_">SourceHttpMessageConverter</span>&lt;&gt;());</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">catch</span> (Error err) &#123;</span><br><span class="line">                <span class="comment">// Ignore when no TransformerFactory implementation is available</span></span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="built_in">this</span>.messageConverters.add(<span class="keyword">new</span> <span class="title class_">AllEncompassingFormHttpMessageConverter</span>());</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    ...</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>应用中<code>WebMvcAutoConfiguration</code>（底层是<code>WebMvcConfigurationSupport</code>实现）传入更多<code>messageConverters</code>，其中就包含<code>MappingJackson2HttpMessageConverter</code>。</p>
<h1 id="内容协商原理"><a href="#内容协商原理" class="headerlink" title="内容协商原理"></a>内容协商原理</h1><p>根据客户端接收能力不同，返回不同媒体类型的数据。</p>
<p>引入XML依赖：</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"> <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">     <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>com.fasterxml.jackson.dataformat<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">     <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>jackson-dataformat-xml<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure>
<p>可用Postman软件分别测试返回json和xml：只需要改变请求头中Accept字段（application/json、application/xml）。</p>
<p>Http协议中规定的，Accept字段告诉服务器本客户端可以接收的数据类型。</p>
<p><strong>内容协商原理</strong>：</p>
<ol>
<li>判断当前响应头中是否已经有确定的媒体类型<code>MediaType</code>。</li>
<li>获取客户端（PostMan、浏览器）支持接收的内容类型。（获取客户端Accept请求头字段application/xml）（这一步在下一节有详细介绍）<ul>
<li><code>contentNegotiationManager</code> 内容协商管理器 默认使用基于请求头的策略</li>
<li><code>HeaderContentNegotiationStrategy</code>  确定客户端可以接收的内容类型 </li>
</ul>
</li>
<li>遍历循环所有当前系统的 <code>MessageConverter</code>，看谁支持操作这个对象（Person）</li>
<li>找到支持操作Person的converter，把converter支持的媒体类型统计出来。</li>
<li>客户端需要application/xml，服务端有10种MediaType。</li>
<li>进行内容协商的最佳匹配媒体类型</li>
<li>用 支持 将对象转为 最佳匹配媒体类型 的converter。调用它进行转化 。</li>
</ol>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//RequestResponseBodyMethodProcessor继承这类</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">class</span> <span class="title class_">AbstractMessageConverterMethodProcessor</span> <span class="keyword">extends</span> <span class="title class_">AbstractMessageConverterMethodArgumentResolver</span></span><br><span class="line">        <span class="keyword">implements</span> <span class="title class_">HandlerMethodReturnValueHandler</span> &#123;</span><br><span class="line"></span><br><span class="line">    ...</span><br><span class="line"></span><br><span class="line">    <span class="comment">//跟上一节的代码一致</span></span><br><span class="line">    <span class="keyword">protected</span> &lt;T&gt; <span class="keyword">void</span> <span class="title function_">writeWithMessageConverters</span><span class="params">(<span class="meta">@Nullable</span> T value, MethodParameter returnType,</span></span><br><span class="line"><span class="params">                ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)</span></span><br><span class="line">                <span class="keyword">throws</span> IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException &#123;</span><br><span class="line"></span><br><span class="line">            Object body;</span><br><span class="line">            Class&lt;?&gt; valueType;</span><br><span class="line">            Type targetType;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span> (value <span class="keyword">instanceof</span> CharSequence) &#123;</span><br><span class="line">                body = value.toString();</span><br><span class="line">                valueType = String.class;</span><br><span class="line">                targetType = String.class;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">else</span> &#123;</span><br><span class="line">                body = value;</span><br><span class="line">                valueType = getReturnValueType(body, returnType);</span><br><span class="line">                targetType = GenericTypeResolver.resolveType(getGenericType(returnType), returnType.getContainingClass());</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            ...</span><br><span class="line"></span><br><span class="line">            <span class="comment">//本节重点</span></span><br><span class="line">            <span class="comment">//内容协商（浏览器默认会以请求头(参数Accept)的方式告诉服务器他能接受什么样的内容类型）</span></span><br><span class="line">            <span class="type">MediaType</span> <span class="variable">selectedMediaType</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">            <span class="type">MediaType</span> <span class="variable">contentType</span> <span class="operator">=</span> outputMessage.getHeaders().getContentType();</span><br><span class="line">            <span class="type">boolean</span> <span class="variable">isContentTypePreset</span> <span class="operator">=</span> contentType != <span class="literal">null</span> &amp;&amp; contentType.isConcrete();</span><br><span class="line">            <span class="keyword">if</span> (isContentTypePreset) &#123;</span><br><span class="line">                <span class="keyword">if</span> (logger.isDebugEnabled()) &#123;</span><br><span class="line">                    logger.debug(<span class="string">&quot;Found &#x27;Content-Type:&quot;</span> + contentType + <span class="string">&quot;&#x27; in response&quot;</span>);</span><br><span class="line">                &#125;</span><br><span class="line">                selectedMediaType = contentType;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="type">HttpServletRequest</span> <span class="variable">request</span> <span class="operator">=</span> inputMessage.getServletRequest();</span><br><span class="line">                List&lt;MediaType&gt; acceptableTypes = getAcceptableMediaTypes(request);</span><br><span class="line">                <span class="comment">//服务器最终根据自己自身的能力，决定服务器能生产出什么样内容类型的数据</span></span><br><span class="line">                List&lt;MediaType&gt; producibleTypes = getProducibleMediaTypes(request, valueType, targetType);</span><br><span class="line"></span><br><span class="line">                <span class="keyword">if</span> (body != <span class="literal">null</span> &amp;&amp; producibleTypes.isEmpty()) &#123;</span><br><span class="line">                    <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">HttpMessageNotWritableException</span>(</span><br><span class="line">                            <span class="string">&quot;No converter found for return value of type: &quot;</span> + valueType);</span><br><span class="line">                &#125;</span><br><span class="line">                List&lt;MediaType&gt; mediaTypesToUse = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">                <span class="keyword">for</span> (MediaType requestedType : acceptableTypes) &#123;</span><br><span class="line">                    <span class="keyword">for</span> (MediaType producibleType : producibleTypes) &#123;</span><br><span class="line">                        <span class="keyword">if</span> (requestedType.isCompatibleWith(producibleType)) &#123;</span><br><span class="line">                            mediaTypesToUse.add(getMostSpecificMediaType(requestedType, producibleType));</span><br><span class="line">                        &#125;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">if</span> (mediaTypesToUse.isEmpty()) &#123;</span><br><span class="line">                    <span class="keyword">if</span> (body != <span class="literal">null</span>) &#123;</span><br><span class="line">                        <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">HttpMediaTypeNotAcceptableException</span>(producibleTypes);</span><br><span class="line">                    &#125;</span><br><span class="line">                    <span class="keyword">if</span> (logger.isDebugEnabled()) &#123;</span><br><span class="line">                        logger.debug(<span class="string">&quot;No match for &quot;</span> + acceptableTypes + <span class="string">&quot;, supported: &quot;</span> + producibleTypes);</span><br><span class="line">                    &#125;</span><br><span class="line">                    <span class="keyword">return</span>;</span><br><span class="line">                &#125;</span><br><span class="line"></span><br><span class="line">                MediaType.sortBySpecificityAndQuality(mediaTypesToUse);</span><br><span class="line"></span><br><span class="line">                <span class="comment">//选择一个MediaType</span></span><br><span class="line">                <span class="keyword">for</span> (MediaType mediaType : mediaTypesToUse) &#123;</span><br><span class="line">                    <span class="keyword">if</span> (mediaType.isConcrete()) &#123;</span><br><span class="line">                        selectedMediaType = mediaType;</span><br><span class="line">                        <span class="keyword">break</span>;</span><br><span class="line">                    &#125;</span><br><span class="line">                    <span class="keyword">else</span> <span class="keyword">if</span> (mediaType.isPresentIn(ALL_APPLICATION_MEDIA_TYPES)) &#123;</span><br><span class="line">                        selectedMediaType = MediaType.APPLICATION_OCTET_STREAM;</span><br><span class="line">                        <span class="keyword">break</span>;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line"></span><br><span class="line">                <span class="keyword">if</span> (logger.isDebugEnabled()) &#123;</span><br><span class="line">                    logger.debug(<span class="string">&quot;Using &#x27;&quot;</span> + selectedMediaType + <span class="string">&quot;&#x27;, given &quot;</span> +</span><br><span class="line">                            acceptableTypes + <span class="string">&quot; and supported &quot;</span> + producibleTypes);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span> (selectedMediaType != <span class="literal">null</span>) &#123;</span><br><span class="line">                selectedMediaType = selectedMediaType.removeQualityValue();</span><br><span class="line">                <span class="comment">//本节主角：HttpMessageConverter</span></span><br><span class="line">                <span class="keyword">for</span> (HttpMessageConverter&lt;?&gt; converter : <span class="built_in">this</span>.messageConverters) &#123;</span><br><span class="line">                    <span class="type">GenericHttpMessageConverter</span> <span class="variable">genericConverter</span> <span class="operator">=</span> (converter <span class="keyword">instanceof</span> GenericHttpMessageConverter ?</span><br><span class="line">                            (GenericHttpMessageConverter&lt;?&gt;) converter : <span class="literal">null</span>);</span><br><span class="line"></span><br><span class="line">                    <span class="comment">//判断是否可写</span></span><br><span class="line">                    <span class="keyword">if</span> (genericConverter != <span class="literal">null</span> ?</span><br><span class="line">                            ((GenericHttpMessageConverter) converter).canWrite(targetType, valueType, selectedMediaType) :</span><br><span class="line">                            converter.canWrite(valueType, selectedMediaType)) &#123;</span><br><span class="line">                        body = getAdvice().beforeBodyWrite(body, returnType, selectedMediaType,</span><br><span class="line">                                (Class&lt;? <span class="keyword">extends</span> <span class="title class_">HttpMessageConverter</span>&lt;?&gt;&gt;) converter.getClass(),</span><br><span class="line">                                inputMessage, outputMessage);</span><br><span class="line">                        <span class="keyword">if</span> (body != <span class="literal">null</span>) &#123;</span><br><span class="line">                            <span class="type">Object</span> <span class="variable">theBody</span> <span class="operator">=</span> body;</span><br><span class="line">                            LogFormatUtils.traceDebug(logger, traceOn -&gt;</span><br><span class="line">                                    <span class="string">&quot;Writing [&quot;</span> + LogFormatUtils.formatValue(theBody, !traceOn) + <span class="string">&quot;]&quot;</span>);</span><br><span class="line">                            addContentDispositionHeader(inputMessage, outputMessage);</span><br><span class="line">                            <span class="comment">//开始写入</span></span><br><span class="line">                            <span class="keyword">if</span> (genericConverter != <span class="literal">null</span>) &#123;</span><br><span class="line">                                genericConverter.write(body, targetType, selectedMediaType, outputMessage);</span><br><span class="line">                            &#125;</span><br><span class="line">                            <span class="keyword">else</span> &#123;</span><br><span class="line">                                ((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage);</span><br><span class="line">                            &#125;</span><br><span class="line">                        &#125;</span><br><span class="line">                        <span class="keyword">else</span> &#123;</span><br><span class="line">                            <span class="keyword">if</span> (logger.isDebugEnabled()) &#123;</span><br><span class="line">                                logger.debug(<span class="string">&quot;Nothing to write: null body&quot;</span>);</span><br><span class="line">                            &#125;</span><br><span class="line">                        &#125;</span><br><span class="line">                        <span class="keyword">return</span>;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            ...</span><br><span class="line">        &#125;</span><br></pre></td></tr></table></figure>
<h2 id="基于请求参数的内容协商原理"><a href="#基于请求参数的内容协商原理" class="headerlink" title="基于请求参数的内容协商原理"></a>基于请求参数的内容协商原理</h2><p>上一节内容协商原理的第二步：</p>
<p>获取客户端（PostMan、浏览器）支持接收的内容类型。（获取客户端Accept请求头字段application/xml）</p>
<ul>
<li><code>contentNegotiationManager</code> 内容协商管理器 默认使用基于请求头的策略</li>
<li><code>HeaderContentNegotiationStrategy</code>  确定客户端可以接收的内容类型 </li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//RequestResponseBodyMethodProcessor继承这类</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">class</span> <span class="title class_">AbstractMessageConverterMethodProcessor</span> <span class="keyword">extends</span> <span class="title class_">AbstractMessageConverterMethodArgumentResolver</span></span><br><span class="line">        <span class="keyword">implements</span> <span class="title class_">HandlerMethodReturnValueHandler</span> &#123;</span><br><span class="line"></span><br><span class="line">    ...</span><br><span class="line"></span><br><span class="line">    <span class="comment">//跟上一节的代码一致</span></span><br><span class="line">    <span class="keyword">protected</span> &lt;T&gt; <span class="keyword">void</span> <span class="title function_">writeWithMessageConverters</span><span class="params">(<span class="meta">@Nullable</span> T value, MethodParameter returnType,</span></span><br><span class="line"><span class="params">                ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)</span></span><br><span class="line">                <span class="keyword">throws</span> IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException &#123;</span><br><span class="line"></span><br><span class="line">            Object body;</span><br><span class="line">            Class&lt;?&gt; valueType;</span><br><span class="line">            Type targetType;</span><br><span class="line"></span><br><span class="line">            ...</span><br><span class="line"></span><br><span class="line">                    <span class="comment">//本节重点</span></span><br><span class="line">            <span class="comment">//内容协商（浏览器默认会以请求头(参数Accept)的方式告诉服务器他能接受什么样的内容类型）</span></span><br><span class="line">            <span class="type">MediaType</span> <span class="variable">selectedMediaType</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">            <span class="type">MediaType</span> <span class="variable">contentType</span> <span class="operator">=</span> outputMessage.getHeaders().getContentType();</span><br><span class="line">            <span class="type">boolean</span> <span class="variable">isContentTypePreset</span> <span class="operator">=</span> contentType != <span class="literal">null</span> &amp;&amp; contentType.isConcrete();</span><br><span class="line">            <span class="keyword">if</span> (isContentTypePreset) &#123;</span><br><span class="line">                <span class="keyword">if</span> (logger.isDebugEnabled()) &#123;</span><br><span class="line">                    logger.debug(<span class="string">&quot;Found &#x27;Content-Type:&quot;</span> + contentType + <span class="string">&quot;&#x27; in response&quot;</span>);</span><br><span class="line">                &#125;</span><br><span class="line">                selectedMediaType = contentType;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="type">HttpServletRequest</span> <span class="variable">request</span> <span class="operator">=</span> inputMessage.getServletRequest();</span><br><span class="line">                List&lt;MediaType&gt; acceptableTypes = getAcceptableMediaTypes(request);</span><br><span class="line">                <span class="comment">//服务器最终根据自己自身的能力，决定服务器能生产出什么样内容类型的数据</span></span><br><span class="line">                List&lt;MediaType&gt; producibleTypes = getProducibleMediaTypes(request, valueType, targetType);</span><br><span class="line">            ...</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//在AbstractMessageConverterMethodArgumentResolver类内</span></span><br><span class="line">       <span class="keyword">private</span> List&lt;MediaType&gt; <span class="title function_">getAcceptableMediaTypes</span><span class="params">(HttpServletRequest request)</span></span><br><span class="line">            <span class="keyword">throws</span> HttpMediaTypeNotAcceptableException &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//内容协商管理器 默认使用基于请求头的策略</span></span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">this</span>.contentNegotiationManager.resolveMediaTypes(<span class="keyword">new</span> <span class="title class_">ServletWebRequest</span>(request));</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ContentNegotiationManager</span> <span class="keyword">implements</span> <span class="title class_">ContentNegotiationStrategy</span>, MediaTypeFileExtensionResolver &#123;</span><br><span class="line"></span><br><span class="line">    ...</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">ContentNegotiationManager</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>(<span class="keyword">new</span> <span class="title class_">HeaderContentNegotiationStrategy</span>());<span class="comment">//内容协商管理器 默认使用基于请求头的策略</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> List&lt;MediaType&gt; <span class="title function_">resolveMediaTypes</span><span class="params">(NativeWebRequest request)</span> <span class="keyword">throws</span> HttpMediaTypeNotAcceptableException &#123;</span><br><span class="line">        <span class="keyword">for</span> (ContentNegotiationStrategy strategy : <span class="built_in">this</span>.strategies) &#123;</span><br><span class="line">            List&lt;MediaType&gt; mediaTypes = strategy.resolveMediaTypes(request);</span><br><span class="line">            <span class="keyword">if</span> (mediaTypes.equals(MEDIA_TYPE_ALL_LIST)) &#123;</span><br><span class="line">                <span class="keyword">continue</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">return</span> mediaTypes;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> MEDIA_TYPE_ALL_LIST;</span><br><span class="line">    &#125;</span><br><span class="line">    ...</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//基于请求头的策略</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">HeaderContentNegotiationStrategy</span> <span class="keyword">implements</span> <span class="title class_">ContentNegotiationStrategy</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * &#123;<span class="doctag">@inheritDoc</span>&#125;</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> HttpMediaTypeNotAcceptableException if the &#x27;Accept&#x27; header cannot be parsed</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> List&lt;MediaType&gt; <span class="title function_">resolveMediaTypes</span><span class="params">(NativeWebRequest request)</span></span><br><span class="line">            <span class="keyword">throws</span> HttpMediaTypeNotAcceptableException &#123;</span><br><span class="line"></span><br><span class="line">        String[] headerValueArray = request.getHeaderValues(HttpHeaders.ACCEPT);</span><br><span class="line">        <span class="keyword">if</span> (headerValueArray == <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> MEDIA_TYPE_ALL_LIST;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        List&lt;String&gt; headerValues = Arrays.asList(headerValueArray);</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            List&lt;MediaType&gt; mediaTypes = MediaType.parseMediaTypes(headerValues);</span><br><span class="line">            MediaType.sortBySpecificityAndQuality(mediaTypes);</span><br><span class="line">            <span class="keyword">return</span> !CollectionUtils.isEmpty(mediaTypes) ? mediaTypes : MEDIA_TYPE_ALL_LIST;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">catch</span> (InvalidMediaTypeException ex) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">HttpMediaTypeNotAcceptableException</span>(</span><br><span class="line">                    <span class="string">&quot;Could not parse &#x27;Accept&#x27; header &quot;</span> + headerValues + <span class="string">&quot;: &quot;</span> + ex.getMessage());</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h3 id="开启浏览器参数方式内容协商功能"><a href="#开启浏览器参数方式内容协商功能" class="headerlink" title="开启浏览器参数方式内容协商功能"></a>开启浏览器参数方式内容协商功能</h3><p>为了方便内容协商，开启基于请求参数的内容协商功能。</p>
<figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="attr">mvc:</span></span><br><span class="line">    <span class="attr">contentnegotiation:</span></span><br><span class="line">      <span class="attr">favor-parameter:</span> <span class="literal">true</span>  <span class="comment">#开启请求参数内容协商模式</span></span><br></pre></td></tr></table></figure>
<p>内容协商管理器，就会多了一个<code>ParameterContentNegotiationStrategy</code>（由Spring容器注入）</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ParameterContentNegotiationStrategy</span> <span class="keyword">extends</span> <span class="title class_">AbstractMappingContentNegotiationStrategy</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="type">String</span> <span class="variable">parameterName</span> <span class="operator">=</span> <span class="string">&quot;format&quot;</span>;<span class="comment">//</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Create an instance with the given map of file extensions and media types.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">ParameterContentNegotiationStrategy</span><span class="params">(Map&lt;String, MediaType&gt; mediaTypes)</span> &#123;</span><br><span class="line">        <span class="built_in">super</span>(mediaTypes);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Set the name of the parameter to use to determine requested media types.</span></span><br><span class="line"><span class="comment">     * &lt;p&gt;By default this is set to &#123;<span class="doctag">@code</span> &quot;format&quot;&#125;.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setParameterName</span><span class="params">(String parameterName)</span> &#123;</span><br><span class="line">        Assert.notNull(parameterName, <span class="string">&quot;&#x27;parameterName&#x27; is required&quot;</span>);</span><br><span class="line">        <span class="built_in">this</span>.parameterName = parameterName;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">getParameterName</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">this</span>.parameterName;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="meta">@Nullable</span></span><br><span class="line">    <span class="keyword">protected</span> String <span class="title function_">getMediaTypeKey</span><span class="params">(NativeWebRequest request)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> request.getParameter(getParameterName());</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//---以下方法在AbstractMappingContentNegotiationStrategy类</span></span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> List&lt;MediaType&gt; <span class="title function_">resolveMediaTypes</span><span class="params">(NativeWebRequest webRequest)</span></span><br><span class="line">            <span class="keyword">throws</span> HttpMediaTypeNotAcceptableException &#123;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> resolveMediaTypeKey(webRequest, getMediaTypeKey(webRequest));</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * An alternative to &#123;<span class="doctag">@link</span> #resolveMediaTypes(NativeWebRequest)&#125; that accepts</span></span><br><span class="line"><span class="comment">     * an already extracted key.</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@since</span> 3.2.16</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> List&lt;MediaType&gt; <span class="title function_">resolveMediaTypeKey</span><span class="params">(NativeWebRequest webRequest, <span class="meta">@Nullable</span> String key)</span></span><br><span class="line">            <span class="keyword">throws</span> HttpMediaTypeNotAcceptableException &#123;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (StringUtils.hasText(key)) &#123;</span><br><span class="line">            <span class="type">MediaType</span> <span class="variable">mediaType</span> <span class="operator">=</span> lookupMediaType(key);</span><br><span class="line">            <span class="keyword">if</span> (mediaType != <span class="literal">null</span>) &#123;</span><br><span class="line">                handleMatch(key, mediaType);</span><br><span class="line">                <span class="keyword">return</span> Collections.singletonList(mediaType);</span><br><span class="line">            &#125;</span><br><span class="line">            mediaType = handleNoMatch(webRequest, key);</span><br><span class="line">            <span class="keyword">if</span> (mediaType != <span class="literal">null</span>) &#123;</span><br><span class="line">                addMapping(key, mediaType);</span><br><span class="line">                <span class="keyword">return</span> Collections.singletonList(mediaType);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> MEDIA_TYPE_ALL_LIST;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>然后，浏览器地址输入带format参数的URL：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">http://localhost:8080/test/person?format=json</span><br><span class="line">或</span><br><span class="line">http://localhost:8080/test/person?format=xml</span><br></pre></td></tr></table></figure>
<p>这样，后端会根据参数format的值，返回对应json或xml格式的数据。</p>
<h2 id="自定义MessageConverter"><a href="#自定义MessageConverter" class="headerlink" title="自定义MessageConverter"></a>自定义MessageConverter</h2><p><strong>实现多协议数据兼容。json、xml、x-guigu</strong>（这个是自创的）</p>
<ol>
<li><p><code>@ResponseBody</code> 响应数据出去 调用 <code>RequestResponseBodyMethodProcessor</code> 处理</p>
</li>
<li><p>Processor 处理方法返回值。通过 <code>MessageConverter</code>处理</p>
</li>
<li><p>所有 <code>MessageConverter</code> 合起来可以支持各种媒体类型数据的操作（读、写）</p>
</li>
<li><p>内容协商找到最终的 <code>messageConverter</code></p>
</li>
</ol>
<p>SpringMVC的什么功能，一个入口给容器中添加一个  <code>WebMvcConfigurer</code></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Configuration(proxyBeanMethods = false)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">WebConfig</span> &#123;</span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> WebMvcConfigurer <span class="title function_">webMvcConfigurer</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">WebMvcConfigurer</span>() &#123;</span><br><span class="line"></span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">extendMessageConverters</span><span class="params">(List&lt;HttpMessageConverter&lt;?&gt;&gt; converters)</span> &#123;</span><br><span class="line">                converters.add(<span class="keyword">new</span> <span class="title class_">GuiguMessageConverter</span>());</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 自定义的Converter</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">GuiguMessageConverter</span> <span class="keyword">implements</span> <span class="title class_">HttpMessageConverter</span>&lt;Person&gt; &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">canRead</span><span class="params">(Class&lt;?&gt; clazz, MediaType mediaType)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">canWrite</span><span class="params">(Class&lt;?&gt; clazz, MediaType mediaType)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> clazz.isAssignableFrom(Person.class);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 服务器要统计所有MessageConverter都能写出哪些内容类型</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * application/x-guigu</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> List&lt;MediaType&gt; <span class="title function_">getSupportedMediaTypes</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> MediaType.parseMediaTypes(<span class="string">&quot;application/x-guigu&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> Person <span class="title function_">read</span><span class="params">(Class&lt;? extends Person&gt; clazz, HttpInputMessage inputMessage)</span> <span class="keyword">throws</span> IOException, HttpMessageNotReadableException &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">write</span><span class="params">(Person person, MediaType contentType, HttpOutputMessage outputMessage)</span> <span class="keyword">throws</span> IOException, HttpMessageNotWritableException &#123;</span><br><span class="line">        <span class="comment">//自定义协议数据的写出</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">data</span> <span class="operator">=</span> person.getUserName()+<span class="string">&quot;;&quot;</span>+person.getAge()+<span class="string">&quot;;&quot;</span>+person.getBirth();</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        <span class="comment">//写出去</span></span><br><span class="line">        <span class="type">OutputStream</span> <span class="variable">body</span> <span class="operator">=</span> outputMessage.getBody();</span><br><span class="line">        body.write(data.getBytes());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.util.Date;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Controller</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ResponseTestController</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 1、浏览器发请求直接返回 xml    [application/xml]        jacksonXmlConverter</span></span><br><span class="line"><span class="comment">     * 2、如果是ajax请求 返回 json   [application/json]      jacksonJsonConverter</span></span><br><span class="line"><span class="comment">     * 3、如果硅谷app发请求，返回自定义协议数据  [appliaction/x-guigu]   xxxxConverter</span></span><br><span class="line"><span class="comment">     *          属性值1;属性值2;</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * 步骤：</span></span><br><span class="line"><span class="comment">     * 1、添加自定义的MessageConverter进系统底层</span></span><br><span class="line"><span class="comment">     * 2、系统底层就会统计出所有MessageConverter能操作哪些类型</span></span><br><span class="line"><span class="comment">     * 3、客户端内容协商 [guigu---&gt;guigu]</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * 作业：如何以参数的方式进行内容协商</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@ResponseBody</span>  <span class="comment">//利用返回值处理器里面的消息转换器进行处理</span></span><br><span class="line">    <span class="meta">@GetMapping(value = &quot;/test/person&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Person <span class="title function_">getPerson</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="type">Person</span> <span class="variable">person</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Person</span>();</span><br><span class="line">        person.setAge(<span class="number">28</span>);</span><br><span class="line">        person.setBirth(<span class="keyword">new</span> <span class="title class_">Date</span>());</span><br><span class="line">        person.setUserName(<span class="string">&quot;zhangsan&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> person;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>用Postman发送<code>/test/person</code>（请求头<code>Accept:application/x-guigu</code>)，将返回自定义协议数据的写出。</p>
<h2 id="浏览器与PostMan内容协商完全适配"><a href="#浏览器与PostMan内容协商完全适配" class="headerlink" title="浏览器与PostMan内容协商完全适配"></a>浏览器与PostMan内容协商完全适配</h2><p>假设你想基于自定义请求参数的自定义内容协商功能。</p>
<p>换句话，在地址栏输入<code>http://localhost:8080/test/person?format=gg</code>返回数据，跟<code>http://localhost:8080/test/person</code>且请求头参数<code>Accept:application/x-guigu</code>的返回自定义协议数据的一致。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Configuration(proxyBeanMethods = false)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">WebConfig</span> <span class="comment">/*implements WebMvcConfigurer*/</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//1、WebMvcConfigurer定制化SpringMVC的功能</span></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> WebMvcConfigurer <span class="title function_">webMvcConfigurer</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">WebMvcConfigurer</span>() &#123;</span><br><span class="line"></span><br><span class="line">            <span class="comment">/**</span></span><br><span class="line"><span class="comment">             * 自定义内容协商策略</span></span><br><span class="line"><span class="comment">             * <span class="doctag">@param</span> configurer</span></span><br><span class="line"><span class="comment">             */</span></span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">configureContentNegotiation</span><span class="params">(ContentNegotiationConfigurer configurer)</span> &#123;</span><br><span class="line">                <span class="comment">//Map&lt;String, MediaType&gt; mediaTypes</span></span><br><span class="line">                Map&lt;String, MediaType&gt; mediaTypes = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;&gt;();</span><br><span class="line">                mediaTypes.put(<span class="string">&quot;json&quot;</span>,MediaType.APPLICATION_JSON);</span><br><span class="line">                mediaTypes.put(<span class="string">&quot;xml&quot;</span>,MediaType.APPLICATION_XML);</span><br><span class="line">                <span class="comment">//自定义媒体类型</span></span><br><span class="line">                mediaTypes.put(<span class="string">&quot;gg&quot;</span>,MediaType.parseMediaType(<span class="string">&quot;application/x-guigu&quot;</span>));</span><br><span class="line">                <span class="comment">//指定支持解析哪些参数对应的哪些媒体类型</span></span><br><span class="line">                <span class="type">ParameterContentNegotiationStrategy</span> <span class="variable">parameterStrategy</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ParameterContentNegotiationStrategy</span>(mediaTypes);</span><br><span class="line"><span class="comment">//                parameterStrategy.setParameterName(&quot;ff&quot;);</span></span><br><span class="line"></span><br><span class="line">                <span class="comment">//还需添加请求头处理策略，否则accept:application/json、application/xml则会失效</span></span><br><span class="line">                <span class="type">HeaderContentNegotiationStrategy</span> <span class="variable">headeStrategy</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">HeaderContentNegotiationStrategy</span>();</span><br><span class="line"></span><br><span class="line">                configurer.strategies(Arrays.asList(parameterStrategy, headeStrategy));</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    ...</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>日后开发要注意，<strong>有可能我们添加的自定义的功能会覆盖默认很多功能，导致一些默认的功能失效。</strong></p>
<h1 id="视图解析-Thymeleaf初体验"><a href="#视图解析-Thymeleaf初体验" class="headerlink" title="视图解析-Thymeleaf初体验"></a>视图解析-Thymeleaf初体验</h1><blockquote>
<p><strong>Thymeleaf</strong> is a modern server-side Java template engine for both web and standalone environments.</p>
<p>Thymeleaf’s main goal is to bring elegant <em>natural templates</em> to your development workflow — HTML that can be correctly displayed in browsers and also work as static prototypes, allowing for stronger collaboration in development teams.</p>
<p>With modules for Spring Framework, a host of integrations with your favourite tools, and the ability to plug in your own functionality, Thymeleaf is ideal for modern-day HTML5 JVM web development — although there is much more it can do.——<a target="_blank" rel="noopener" href="https://www.thymeleaf.org/">Link</a></p>
</blockquote>
<p><a target="_blank" rel="noopener" href="https://www.thymeleaf.org/documentation.html">Thymeleaf官方文档</a></p>
<h2 id="thymeleaf使用"><a href="#thymeleaf使用" class="headerlink" title="thymeleaf使用"></a>thymeleaf使用</h2><h4 id="引入Starter"><a href="#引入Starter" class="headerlink" title="引入Starter"></a>引入Starter</h4><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-thymeleaf<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure>
<h4 id="自动配置好了thymeleaf"><a href="#自动配置好了thymeleaf" class="headerlink" title="自动配置好了thymeleaf"></a>自动配置好了thymeleaf</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Configuration(proxyBeanMethods = false)</span></span><br><span class="line"><span class="meta">@EnableConfigurationProperties(ThymeleafProperties.class)</span></span><br><span class="line"><span class="meta">@ConditionalOnClass(&#123; TemplateMode.class, SpringTemplateEngine.class &#125;)</span></span><br><span class="line"><span class="meta">@AutoConfigureAfter(&#123; WebMvcAutoConfiguration.class, WebFluxAutoConfiguration.class &#125;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ThymeleafAutoConfiguration</span> &#123;</span><br><span class="line">    ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>自动配好的策略</p>
<ol>
<li><p>所有thymeleaf的配置值都在 ThymeleafProperties</p>
</li>
<li><p>配置好了 <strong>SpringTemplateEngine</strong> </p>
</li>
<li><p>配好了 <strong>ThymeleafViewResolver</strong> </p>
</li>
<li><p>我们只需要直接开发页面</p>
</li>
</ol>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">DEFAULT_PREFIX</span> <span class="operator">=</span> <span class="string">&quot;classpath:/templates/&quot;</span>;<span class="comment">//模板放置处</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">DEFAULT_SUFFIX</span> <span class="operator">=</span> <span class="string">&quot;.html&quot;</span>;<span class="comment">//文件的后缀名</span></span><br></pre></td></tr></table></figure>
<p>编写一个控制层：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Controller</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ViewTestController</span> &#123;</span><br><span class="line">    <span class="meta">@GetMapping(&quot;/hello&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">hello</span><span class="params">(Model model)</span>&#123;</span><br><span class="line">        <span class="comment">//model中的数据会被放在请求域中 request.setAttribute(&quot;a&quot;,aa)</span></span><br><span class="line">        model.addAttribute(<span class="string">&quot;msg&quot;</span>,<span class="string">&quot;一定要大力发展工业文化&quot;</span>);</span><br><span class="line">        model.addAttribute(<span class="string">&quot;link&quot;</span>,<span class="string">&quot;http://www.baidu.com&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;success&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p><code>/templates/success.html</code>：</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;!DOCTYPE <span class="keyword">html</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">html</span> <span class="attr">lang</span>=<span class="string">&quot;en&quot;</span> <span class="attr">xmlns:th</span>=<span class="string">&quot;http://www.thymeleaf.org&quot;</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">head</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">meta</span> <span class="attr">charset</span>=<span class="string">&quot;UTF-8&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">title</span>&gt;</span>Title<span class="tag">&lt;/<span class="name">title</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">head</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">body</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">h1</span> <span class="attr">th:text</span>=<span class="string">&quot;$&#123;msg&#125;&quot;</span>&gt;</span>nice<span class="tag">&lt;/<span class="name">h1</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">h2</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">a</span> <span class="attr">href</span>=<span class="string">&quot;www.baidu.com&quot;</span> <span class="attr">th:href</span>=<span class="string">&quot;$&#123;link&#125;&quot;</span>&gt;</span>去百度<span class="tag">&lt;/<span class="name">a</span>&gt;</span>  <span class="tag">&lt;<span class="name">br</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">a</span> <span class="attr">href</span>=<span class="string">&quot;www.google.com&quot;</span> <span class="attr">th:href</span>=<span class="string">&quot;@&#123;/link&#125;&quot;</span>&gt;</span>去百度<span class="tag">&lt;/<span class="name">a</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">h2</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">body</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">html</span>&gt;</span></span><br></pre></td></tr></table></figure>
<hr>
<figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">server:</span></span><br><span class="line">  <span class="attr">servlet:</span></span><br><span class="line">    <span class="attr">context-path:</span> <span class="string">/app</span> <span class="comment">#设置应用名</span></span><br></pre></td></tr></table></figure>
<p>这个设置后，URL要插入<code>/app</code>,  如<code>http://localhost:8080/app/hello.html</code>。</p>
<h2 id="基本语法-1"><a href="#基本语法-1" class="headerlink" title="基本语法"></a>基本语法</h2><h4 id="表达式"><a href="#表达式" class="headerlink" title="表达式"></a>表达式</h4><div class="table-container">
<table>
<thead>
<tr>
<th>表达式名字</th>
<th>语法</th>
<th style="text-align:center">用途</th>
</tr>
</thead>
<tbody>
<tr>
<td>变量取值</td>
<td>${…}</td>
<td style="text-align:center">获取请求域、session域、对象等值</td>
</tr>
<tr>
<td>选择变量</td>
<td>*{…}</td>
<td style="text-align:center">获取上下文对象值</td>
</tr>
<tr>
<td>消息</td>
<td>#{…}</td>
<td style="text-align:center">获取国际化等值</td>
</tr>
<tr>
<td>链接</td>
<td>@{…}</td>
<td style="text-align:center">生成链接</td>
</tr>
<tr>
<td>片段表达式</td>
<td>~{…}</td>
<td style="text-align:center">jsp:include 作用，引入公共页面片段</td>
</tr>
</tbody>
</table>
</div>
<h4 id="字面量"><a href="#字面量" class="headerlink" title="字面量"></a>字面量</h4><ul>
<li>文本值: <strong>‘one text’</strong> <strong>,</strong> <strong>‘Another one!’</strong> <strong>,…</strong></li>
<li>数字: <strong>0</strong> <strong>,</strong> <strong>34</strong> <strong>,</strong> <strong>3.0</strong> <strong>,</strong> <strong>12.3</strong> <strong>,…</strong></li>
<li>布尔值: <strong>true</strong> <strong>,</strong> <strong>false</strong></li>
<li>空值: <strong>null</strong></li>
<li>变量： one，two，…. 变量不能有空格</li>
</ul>
<h4 id="文本操作"><a href="#文本操作" class="headerlink" title="文本操作"></a>文本操作</h4><ul>
<li>字符串拼接: <strong>+</strong></li>
<li>变量替换: <strong>|The name is ${name}|</strong> </li>
</ul>
<h4 id="数学运算"><a href="#数学运算" class="headerlink" title="数学运算"></a>数学运算</h4><ul>
<li>运算符: + , - , * , / , %</li>
</ul>
<h4 id="布尔运算"><a href="#布尔运算" class="headerlink" title="布尔运算"></a>布尔运算</h4><ul>
<li>运算符:  <strong>and</strong> <strong>,</strong> <strong>or</strong></li>
<li>一元运算: <strong>!</strong> <strong>,</strong> <strong>not</strong> </li>
</ul>
<h4 id="比较运算"><a href="#比较运算" class="headerlink" title="比较运算"></a>比较运算</h4><ul>
<li>比较: <strong>&gt;</strong> <strong>,</strong> <strong>&lt;</strong> <strong>,</strong> <strong>&gt;=</strong> <strong>,</strong> <strong>&lt;=</strong> <strong>(</strong> <strong>gt</strong> <strong>,</strong> <strong>lt</strong> <strong>,</strong> <strong>ge</strong> <strong>,</strong> <strong>le</strong> <strong>)</strong></li>
<li>等式: <strong>==</strong> <strong>,</strong> <strong>!=</strong> <strong>(</strong> <strong>eq</strong> <strong>,</strong> <strong>ne</strong> <strong>)</strong> </li>
</ul>
<h4 id="条件运算"><a href="#条件运算" class="headerlink" title="条件运算"></a>条件运算</h4><ul>
<li>If-then: <strong>(if) ? (then)</strong></li>
<li>If-then-else: <strong>(if) ? (then) : (else)</strong></li>
<li>Default: (value) <strong>?: (defaultvalue)</strong> </li>
</ul>
<h4 id="特殊操作"><a href="#特殊操作" class="headerlink" title="特殊操作"></a>特殊操作</h4><ul>
<li>无操作： _</li>
</ul>
<h2 id="设置属性值-th-attr"><a href="#设置属性值-th-attr" class="headerlink" title="设置属性值-th:attr"></a>设置属性值-th:attr</h2><ul>
<li>设置单个值</li>
</ul>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">form</span> <span class="attr">action</span>=<span class="string">&quot;subscribe.html&quot;</span> <span class="attr">th:attr</span>=<span class="string">&quot;action=@&#123;/subscribe&#125;&quot;</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">fieldset</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">input</span> <span class="attr">type</span>=<span class="string">&quot;text&quot;</span> <span class="attr">name</span>=<span class="string">&quot;email&quot;</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">input</span> <span class="attr">type</span>=<span class="string">&quot;submit&quot;</span> <span class="attr">value</span>=<span class="string">&quot;Subscribe!&quot;</span> <span class="attr">th:attr</span>=<span class="string">&quot;value=#&#123;subscribe.submit&#125;&quot;</span>/&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">fieldset</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">form</span>&gt;</span></span><br></pre></td></tr></table></figure>
<ul>
<li>设置多个值</li>
</ul>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">img</span> <span class="attr">src</span>=<span class="string">&quot;../../images/gtvglogo.png&quot;</span>  </span></span><br><span class="line"><span class="tag">     <span class="attr">th:attr</span>=<span class="string">&quot;src=@&#123;/images/gtvglogo.png&#125;,title=#&#123;logo&#125;,alt=#&#123;logo&#125;&quot;</span> /&gt;</span></span><br></pre></td></tr></table></figure>
<p><a target="_blank" rel="noopener" href="https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html#setting-attribute-values">官方文档 - 5 Setting Attribute Values</a></p>
<h2 id="迭代"><a href="#迭代" class="headerlink" title="迭代"></a>迭代</h2><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">tr</span> <span class="attr">th:each</span>=<span class="string">&quot;prod : $&#123;prods&#125;&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">td</span> <span class="attr">th:text</span>=<span class="string">&quot;$&#123;prod.name&#125;&quot;</span>&gt;</span>Onions<span class="tag">&lt;/<span class="name">td</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">td</span> <span class="attr">th:text</span>=<span class="string">&quot;$&#123;prod.price&#125;&quot;</span>&gt;</span>2.41<span class="tag">&lt;/<span class="name">td</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">td</span> <span class="attr">th:text</span>=<span class="string">&quot;$&#123;prod.inStock&#125;? #&#123;true&#125; : #&#123;false&#125;&quot;</span>&gt;</span>yes<span class="tag">&lt;/<span class="name">td</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">tr</span>&gt;</span></span><br></pre></td></tr></table></figure>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">tr</span> <span class="attr">th:each</span>=<span class="string">&quot;prod,iterStat : $&#123;prods&#125;&quot;</span> <span class="attr">th:class</span>=<span class="string">&quot;$&#123;iterStat.odd&#125;? &#x27;odd&#x27;&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">td</span> <span class="attr">th:text</span>=<span class="string">&quot;$&#123;prod.name&#125;&quot;</span>&gt;</span>Onions<span class="tag">&lt;/<span class="name">td</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">td</span> <span class="attr">th:text</span>=<span class="string">&quot;$&#123;prod.price&#125;&quot;</span>&gt;</span>2.41<span class="tag">&lt;/<span class="name">td</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">td</span> <span class="attr">th:text</span>=<span class="string">&quot;$&#123;prod.inStock&#125;? #&#123;true&#125; : #&#123;false&#125;&quot;</span>&gt;</span>yes<span class="tag">&lt;/<span class="name">td</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">tr</span>&gt;</span></span><br></pre></td></tr></table></figure>
<h2 id="条件运算-1"><a href="#条件运算-1" class="headerlink" title="条件运算"></a>条件运算</h2><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">a</span> <span class="attr">href</span>=<span class="string">&quot;comments.html&quot;</span></span></span><br><span class="line"><span class="tag">    <span class="attr">th:href</span>=<span class="string">&quot;@&#123;/product/comments(prodId=$&#123;prod.id&#125;)&#125;&quot;</span></span></span><br><span class="line"><span class="tag">    <span class="attr">th:if</span>=<span class="string">&quot;$&#123;not #lists.isEmpty(prod.comments)&#125;&quot;</span>&gt;</span>view<span class="tag">&lt;/<span class="name">a</span>&gt;</span></span><br></pre></td></tr></table></figure>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">th:switch</span>=<span class="string">&quot;$&#123;user.role&#125;&quot;</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">p</span> <span class="attr">th:case</span>=<span class="string">&quot;&#x27;admin&#x27;&quot;</span>&gt;</span>User is an administrator<span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">p</span> <span class="attr">th:case</span>=<span class="string">&quot;#&#123;roles.manager&#125;&quot;</span>&gt;</span>User is a manager<span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">p</span> <span class="attr">th:case</span>=<span class="string">&quot;*&quot;</span>&gt;</span>User is some other thing<span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br></pre></td></tr></table></figure>
<h2 id="属性优先级"><a href="#属性优先级" class="headerlink" title="属性优先级"></a>属性优先级</h2><div class="table-container">
<table>
<thead>
<tr>
<th style="text-align:left">Order</th>
<th style="text-align:left">Feature</th>
<th style="text-align:left">Attributes</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:left">1</td>
<td style="text-align:left">Fragment inclusion</td>
<td style="text-align:left"><code>th:insert</code> <code>th:replace</code></td>
</tr>
<tr>
<td style="text-align:left">2</td>
<td style="text-align:left">Fragment iteration</td>
<td style="text-align:left"><code>th:each</code></td>
</tr>
<tr>
<td style="text-align:left">3</td>
<td style="text-align:left">Conditional evaluation</td>
<td style="text-align:left"><code>th:if</code> <code>th:unless</code> <code>th:switch</code> <code>th:case</code></td>
</tr>
<tr>
<td style="text-align:left">4</td>
<td style="text-align:left">Local variable definition</td>
<td style="text-align:left"><code>th:object</code> <code>th:with</code></td>
</tr>
<tr>
<td style="text-align:left">5</td>
<td style="text-align:left">General attribute modification</td>
<td style="text-align:left"><code>th:attr</code> <code>th:attrprepend</code> <code>th:attrappend</code></td>
</tr>
<tr>
<td style="text-align:left">6</td>
<td style="text-align:left">Specific attribute modification</td>
<td style="text-align:left"><code>th:value</code> <code>th:href</code> <code>th:src</code> <code>...</code></td>
</tr>
<tr>
<td style="text-align:left">7</td>
<td style="text-align:left">Text (tag body modification)</td>
<td style="text-align:left"><code>th:text</code> <code>th:utext</code></td>
</tr>
<tr>
<td style="text-align:left">8</td>
<td style="text-align:left">Fragment specification</td>
<td style="text-align:left"><code>th:fragment</code></td>
</tr>
<tr>
<td style="text-align:left">9</td>
<td style="text-align:left">Fragment removal</td>
<td style="text-align:left"><code>th:remove</code></td>
</tr>
</tbody>
</table>
</div>
<p><a target="_blank" rel="noopener" href="https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html#attribute-precedence">官方文档 - 10 Attribute Precedence</a></p>
<h1 id="后台管理系统基本功能"><a href="#后台管理系统基本功能" class="headerlink" title="后台管理系统基本功能"></a>后台管理系统基本功能</h1><h2 id="项目创建"><a href="#项目创建" class="headerlink" title="项目创建"></a>项目创建</h2><p>使用IDEA的Spring Initializr。</p>
<ul>
<li>thymeleaf、</li>
<li>web-starter、</li>
<li>devtools、</li>
<li>lombok</li>
</ul>
<h2 id="登陆页面"><a href="#登陆页面" class="headerlink" title="登陆页面"></a>登陆页面</h2><ul>
<li><p><code>/static</code> 放置 css，js等静态资源</p>
</li>
<li><p><code>/templates/login.html</code> 登录页</p>
</li>
</ul>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">html</span> <span class="attr">lang</span>=<span class="string">&quot;en&quot;</span> <span class="attr">xmlns:th</span>=<span class="string">&quot;http://www.thymeleaf.org&quot;</span>&gt;</span><span class="comment">&lt;!-- 要加这玩意thymeleaf才能用 --&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">form</span> <span class="attr">class</span>=<span class="string">&quot;form-signin&quot;</span> <span class="attr">action</span>=<span class="string">&quot;index.html&quot;</span> <span class="attr">method</span>=<span class="string">&quot;post&quot;</span> <span class="attr">th:action</span>=<span class="string">&quot;@&#123;/login&#125;&quot;</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    ...</span><br><span class="line"></span><br><span class="line">    <span class="comment">&lt;!-- 消息提醒 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">label</span> <span class="attr">style</span>=<span class="string">&quot;color: red&quot;</span> <span class="attr">th:text</span>=<span class="string">&quot;$&#123;msg&#125;&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">label</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">input</span> <span class="attr">type</span>=<span class="string">&quot;text&quot;</span> <span class="attr">name</span>=<span class="string">&quot;userName&quot;</span> <span class="attr">class</span>=<span class="string">&quot;form-control&quot;</span> <span class="attr">placeholder</span>=<span class="string">&quot;User ID&quot;</span> <span class="attr">autofocus</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">input</span> <span class="attr">type</span>=<span class="string">&quot;password&quot;</span> <span class="attr">name</span>=<span class="string">&quot;password&quot;</span> <span class="attr">class</span>=<span class="string">&quot;form-control&quot;</span> <span class="attr">placeholder</span>=<span class="string">&quot;Password&quot;</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">button</span> <span class="attr">class</span>=<span class="string">&quot;btn btn-lg btn-login btn-block&quot;</span> <span class="attr">type</span>=<span class="string">&quot;submit&quot;</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">i</span> <span class="attr">class</span>=<span class="string">&quot;fa fa-check&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">i</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">button</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    ...</span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;/<span class="name">form</span>&gt;</span></span><br></pre></td></tr></table></figure>
<ul>
<li><code>/templates/main.html</code> 主页</li>
</ul>
<p>thymeleaf内联写法：</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">p</span>&gt;</span>Hello, [[$&#123;session.user.name&#125;]]!<span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br></pre></td></tr></table></figure>
<h2 id="登录控制层"><a href="#登录控制层" class="headerlink" title="登录控制层"></a>登录控制层</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Controller</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">IndexController</span> &#123;</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 来登录页</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@GetMapping(value = &#123;&quot;/&quot;,&quot;/login&quot;&#125;)</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">loginPage</span><span class="params">()</span>&#123;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;login&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@PostMapping(&quot;/login&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">main</span><span class="params">(User user, HttpSession session, Model model)</span>&#123; <span class="comment">//RedirectAttributes</span></span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span>(StringUtils.hasLength(user.getUserName()) &amp;&amp; <span class="string">&quot;123456&quot;</span>.equals(user.getPassword()))&#123;</span><br><span class="line">            <span class="comment">//把登陆成功的用户保存起来</span></span><br><span class="line">            session.setAttribute(<span class="string">&quot;loginUser&quot;</span>,user);</span><br><span class="line">            <span class="comment">//登录成功重定向到main.html;  重定向防止表单重复提交</span></span><br><span class="line">            <span class="keyword">return</span> <span class="string">&quot;redirect:/main.html&quot;</span>;</span><br><span class="line">        &#125;<span class="keyword">else</span> &#123;</span><br><span class="line">            model.addAttribute(<span class="string">&quot;msg&quot;</span>,<span class="string">&quot;账号密码错误&quot;</span>);</span><br><span class="line">            <span class="comment">//回到登录页面</span></span><br><span class="line">            <span class="keyword">return</span> <span class="string">&quot;login&quot;</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">     <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 去main页面</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@GetMapping(&quot;/main.html&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">mainPage</span><span class="params">(HttpSession session, Model model)</span>&#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//最好用拦截器,过滤器</span></span><br><span class="line">        <span class="type">Object</span> <span class="variable">loginUser</span> <span class="operator">=</span> session.getAttribute(<span class="string">&quot;loginUser&quot;</span>);</span><br><span class="line">        <span class="keyword">if</span>(loginUser != <span class="literal">null</span>)&#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="string">&quot;main&quot;</span>;</span><br><span class="line">        &#125;<span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="comment">//session过期，没有登陆过</span></span><br><span class="line">            <span class="comment">//回到登录页面</span></span><br><span class="line">            model.addAttribute(<span class="string">&quot;msg&quot;</span>,<span class="string">&quot;请重新登录&quot;</span>);</span><br><span class="line">            <span class="keyword">return</span> <span class="string">&quot;login&quot;</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="模型"><a href="#模型" class="headerlink" title="模型"></a>模型</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@AllArgsConstructor</span></span><br><span class="line"><span class="meta">@NoArgsConstructor</span></span><br><span class="line"><span class="meta">@Data</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">User</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> String userName;</span><br><span class="line">    <span class="keyword">private</span> String password;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
 
      <!-- reward -->
      
      <div id="reword-out">
        <div id="reward-btn">
          打赏
        </div>
      </div>
      
    </div>
    

    <!-- copyright -->
    
    <div class="declare">
      <ul class="post-copyright">
        <li>
          <i class="ri-copyright-line"></i>
          <strong>版权声明： </strong>
          
          本博客所有文章除特别声明外，著作权归作者所有。转载请注明出处！
          
        </li>
      </ul>
    </div>
    
    <footer class="article-footer">
       
<div class="share-btn">
      <span class="share-sns share-outer">
        <i class="ri-share-forward-line"></i>
        分享
      </span>
      <div class="share-wrap">
        <i class="arrow"></i>
        <div class="share-icons">
          
          <a class="weibo share-sns" href="javascript:;" data-type="weibo">
            <i class="ri-weibo-fill"></i>
          </a>
          <a class="weixin share-sns wxFab" href="javascript:;" data-type="weixin">
            <i class="ri-wechat-fill"></i>
          </a>
          <a class="qq share-sns" href="javascript:;" data-type="qq">
            <i class="ri-qq-fill"></i>
          </a>
          <a class="douban share-sns" href="javascript:;" data-type="douban">
            <i class="ri-douban-line"></i>
          </a>
          <!-- <a class="qzone share-sns" href="javascript:;" data-type="qzone">
            <i class="icon icon-qzone"></i>
          </a> -->
          
          <a class="facebook share-sns" href="javascript:;" data-type="facebook">
            <i class="ri-facebook-circle-fill"></i>
          </a>
          <a class="twitter share-sns" href="javascript:;" data-type="twitter">
            <i class="ri-twitter-fill"></i>
          </a>
          <a class="google share-sns" href="javascript:;" data-type="google">
            <i class="ri-google-fill"></i>
          </a>
        </div>
      </div>
</div>

<div class="wx-share-modal">
    <a class="modal-close" href="javascript:;"><i class="ri-close-circle-line"></i></a>
    <p>扫一扫，分享到微信</p>
    <div class="wx-qrcode">
      <img src="//api.qrserver.com/v1/create-qr-code/?size=150x150&data=https://blog.shimmerjordan.eu.org/2022/07/03/Spring-boot-dict-1/" alt="微信分享二维码">
    </div>
</div>

<div id="share-mask"></div>  
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/Spring/" rel="tag">Spring</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/SpringBoot/" rel="tag">SpringBoot</a></li></ul>

    </footer>
  </div>

   
  <nav class="article-nav">
    
      <a href="/2022/07/03/Spring-boot-dict-2/" class="article-nav-link">
        <strong class="article-nav-caption">上一篇</strong>
        <div class="article-nav-title">
          
            SpringBoot2 扎记下篇
          
        </div>
      </a>
    
    
      <a href="/2022/07/02/Mybatis-dict/" class="article-nav-link">
        <strong class="article-nav-caption">下一篇</strong>
        <div class="article-nav-title">Mybatis学习乱记</div>
      </a>
    
  </nav>

   
<!-- valine评论 -->
<div id="vcomments-box">
  <div id="vcomments"></div>
</div>
<script src="//cdn1.lncld.net/static/js/3.0.4/av-min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/valine@1.4.14/dist/Valine.min.js"></script>
<script>
  new Valine({
    el: "#vcomments",
    app_id: "StYfTMDp78X0EltFR16ve2q5-gzGzoHsz",
    app_key: "G4RPxRpXG6RwdfpnJefOSnyy",
    path: window.location.pathname,
    avatar: "wavatar",
    placeholder: "ヾﾉ≧∀≦)o来啊，快活啊!",
    recordIP: true,
  });
  const infoEle = document.querySelector("#vcomments .info");
  if (infoEle && infoEle.childNodes && infoEle.childNodes.length > 0) {
    infoEle.childNodes.forEach(function (item) {
      item.parentNode.removeChild(item);
    });
  }
</script>
<style>
  #vcomments-box {
    padding: 5px 30px;
  }

  @media screen and (max-width: 800px) {
    #vcomments-box {
      padding: 5px 0px;
    }
  }

  #vcomments-box #vcomments {
    background-color: #fff;
  }

  .v .vlist .vcard .vh {
    padding-right: 20px;
  }

  .v .vlist .vcard {
    padding-left: 10px;
  }
</style>

 
   
   
<!-- minivaline评论 -->
<div id="mvcomments-box">
  <div id="mvcomments"></div>
</div>
<script src="https://cdn.jsdelivr.net/npm/minivaline@latest"></script>
<script>
    new MiniValine(Object.assign({"enable":true,"mode":"DesertsP","placeholder":"Write a Comment","math":true,"md":true,"enableQQ":true,"NoRecordIP":false,"visitor":true,"maxNest":6,"pageSize":6,"adminEmailMd5":"de8a7aa53d07e6b6bceb45c64027763d","tagMeta":["管理员","小伙伴","访客"],"master":["de8a7aa53d07e6b6bceb45c64027763d"],"friends":["b5bd5d836c7a0091aa8473e79ed4c25e","adb7d1cd192658a55c0ad22a3309cecf","3ce1e6c77b4910f1871106cb30dc62b0","cfce8dc43725cc14ffcd9fb4892d5bfc"],"lang":null,"emoticonUrl":["https://cdn.jsdelivr.net/npm/alus@latest","https://cdn.jsdelivr.net/gh/MiniValine/qq@latest","https://cdn.jsdelivr.net/gh/MiniValine/Bilibilis@latest","https://cdn.jsdelivr.net/gh/MiniValine/tieba@latest","https://cdn.jsdelivr.net/gh/MiniValine/twemoji@latest","https://cdn.jsdelivr.net/gh/MiniValine/weibo@latest"]}, {
	  el: '#mvcomments',
    }));
  const infoEle = document.querySelector('#mvcomments .info');
  if (infoEle && infoEle.childNodes && infoEle.childNodes.length > 0) {
      infoEle.childNodes.forEach(function (item) {
          item.parentNode.removeChild(item);
      });
  }
</script>
<style>
	#mvcomments-box {
		padding: 5px 30px;
	}
	@media screen and (max-width: 800px) {
		#mvcomments-box {
		  padding: 5px 0px;
		}
	}
	.darkmode .MiniValine *{
		color: #f1f1f1!important;
	}
	.darkmode .commentTrigger{
		background-color: #403e3e !important;
	  }
	.darkmode .MiniValine .vpage .more{
		background: #21232F
	}
	.darkmode img{
		filter: brightness(30%)
	}
	.darkmode .MiniValine .vlist .vcard .vcomment-body .text-wrapper .vcomment.expand:before{
		background: linear-gradient(180deg, rgba(246,246,246,0), rgba(0,0,0,0.9))
	}
	.darkmode .MiniValine .vlist .vcard .vcomment-body .text-wrapper .vcomment.expand:after{
		background: rgba(0,0,0,0.9)
	}
	.darkmode .MiniValine .vlist .vcard .vcomment-body .text-wrapper .vcomment pre{
		background: #282c34
		border: 1px solid #282c34
	}
	.darkmode .MiniValine .vinputs-area .textarea-wrapper textarea{
		color: #000;
	}
	.darkmode .MiniValine .vinputs-area .auth-section .input-wrapper input{
		color: #000;
	}
	.darkmode .MiniValine .vinputs-area .vextra-area .vsmile-icons{
		background: transparent;
	}
	.darkmode .MiniValine .vinputs-wrap{
		border-color: #b2b2b5;
	}
	.darkmode .MiniValine .vinputs-wrap:hover{
		border: 1px dashed #2196f3;
	}
	.darkmode .MiniValine .vinputs-area .auth-section .input-wrapper{
		border-bottom: 1px dashed #b2b2b5;
	}
	.darkmode .MiniValine .vinputs-area .auth-section .input-wrapper:hover{
		border-bottom: 1px dashed #2196f3;
	}
	.darkmode .MiniValine .vbtn{
		background-color: transparent!important;
	}
	.darkmode .MiniValine .vbtn:hover{
		border: 1px dashed #2196f3;
	}
</style>

    
</article>

</section>
      <footer class="footer">
  <div class="outer">
    <ul>
      <li>
        Copyrights &copy;
        2019-2024
        <i class="ri-heart-fill heart_icon"></i> 鞠桥丹-QIAODAN JU
      </li>
    </ul>
    <ul>
      <li>
        
        
        
        由 <a href="https://hexo.io" target="_blank">Hexo</a> 强力驱动
        <span class="division">|</span>
        主题 - <a href="https://github.com/Shen-Yu/hexo-theme-ayer" target="_blank">Ayer</a>
        
      </li>
    </ul>
    <ul>
      <li>
        
        
        <span>
  <span><i class="ri-user-3-fill"></i>访问人数:<span id="busuanzi_value_site_uv"></span></s>
  <span class="division">|</span>
  <span><i class="ri-eye-fill"></i>浏览次数:<span id="busuanzi_value_page_pv"></span></span>
</span>
        
      </li>
    </ul>
    <ul>
      
    </ul>
    <ul>
      
    </ul>
    <ul>
      <li>
        <!-- cnzz统计 -->
        
        <script type="text/javascript" src='https://s4.cnzz.com/z_stat.php?id=1279035150&amp;web_id=1279035150'></script>
        
      </li>
    </ul>
  </div>
</footer>
      <div class="float_btns">
        <div class="totop" id="totop">
  <i class="ri-arrow-up-line"></i>
</div>

<div class="todark" id="todark">
  <i class="ri-moon-line"></i>
</div>

      </div>
    </main>
    <aside class="sidebar on">
      <button class="navbar-toggle"></button>
<nav class="navbar">
  
  <div class="logo">
    <a href="/"><img src="/images/ayer-side.svg" alt="丛烨-shimmerjordan"></a>
  </div>
  
  <ul class="nav nav-main">
    
    <li class="nav-item">
      <a class="nav-item-link" href="/">Home</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/archives">Catalogue</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/tags">Tags</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/tags/%E9%9A%8F%E7%AC%94/">Essay</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/categories">Archives</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/friends">Friends</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/2020/01/18/about">About</a>
    </li>
    
  </ul>
</nav>
<nav class="navbar navbar-bottom">
  <ul class="nav">
    <li class="nav-item">
      
      <a class="nav-item-link nav-item-search"  title="搜索">
        <i class="ri-search-line"></i>
      </a>
      
      
      <a class="nav-item-link" target="_blank" href="/atom.xml" title="RSS Feed">
        <i class="ri-rss-line"></i>
      </a>
      
    </li>
  </ul>
</nav>
<div class="search-form-wrap">
  <div class="local-search local-search-plugin">
  <input type="search" id="local-search-input" class="local-search-input" placeholder="Search...">
  <div id="local-search-result" class="local-search-result"></div>
</div>
</div>
    </aside>
    <script>
      if (window.matchMedia("(max-width: 768px)").matches) {
        document.querySelector('.content').classList.remove('on');
        document.querySelector('.sidebar').classList.remove('on');
      }
    </script>
    <div id="mask"></div>

<!-- #reward -->
<div id="reward">
  <span class="close"><i class="ri-close-line"></i></span>
  <p class="reward-p"><i class="ri-cup-line"></i>请我喝杯蓝莓汁吧~</p>
  <div class="reward-box">
    
    <div class="reward-item">
      <img class="reward-img" src="/images/alipay.jpg">
      <span class="reward-type">支付宝</span>
    </div>
    
    
    <div class="reward-item">
      <img class="reward-img" src="/images/wechat.jpg">
      <span class="reward-type">微信</span>
    </div>
    
  </div>
</div>
    
<script src="/js/jquery-2.0.3.min.js"></script>


<script src="/js/lazyload.min.js"></script>

<!-- Tocbot -->


<script src="/js/tocbot.min.js"></script>

<script>
  tocbot.init({
    tocSelector: '.tocbot',
    contentSelector: '.article-entry',
    headingSelector: 'h1, h2, h3, h4, h5, h6',
    hasInnerContainers: true,
    scrollSmooth: true,
    scrollContainer: 'main',
    positionFixedSelector: '.tocbot',
    positionFixedClass: 'is-position-fixed',
    fixedSidebarOffset: 'auto'
  });
</script>

<script src="https://cdn.jsdelivr.net/npm/jquery-modal@0.9.2/jquery.modal.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/jquery-modal@0.9.2/jquery.modal.min.css">
<script src="https://cdn.jsdelivr.net/npm/justifiedGallery@3.7.0/dist/js/jquery.justifiedGallery.min.js"></script>

<script src="/dist/main.js"></script>

<!-- ImageViewer -->

<!-- Root element of PhotoSwipe. Must have class pswp. -->
<div class="pswp" tabindex="-1" role="dialog" aria-hidden="true">

    <!-- Background of PhotoSwipe. 
         It's a separate element as animating opacity is faster than rgba(). -->
    <div class="pswp__bg"></div>

    <!-- Slides wrapper with overflow:hidden. -->
    <div class="pswp__scroll-wrap">

        <!-- Container that holds slides. 
            PhotoSwipe keeps only 3 of them in the DOM to save memory.
            Don't modify these 3 pswp__item elements, data is added later on. -->
        <div class="pswp__container">
            <div class="pswp__item"></div>
            <div class="pswp__item"></div>
            <div class="pswp__item"></div>
        </div>

        <!-- Default (PhotoSwipeUI_Default) interface on top of sliding area. Can be changed. -->
        <div class="pswp__ui pswp__ui--hidden">

            <div class="pswp__top-bar">

                <!--  Controls are self-explanatory. Order can be changed. -->

                <div class="pswp__counter"></div>

                <button class="pswp__button pswp__button--close" title="Close (Esc)"></button>

                <button class="pswp__button pswp__button--share" style="display:none" title="Share"></button>

                <button class="pswp__button pswp__button--fs" title="Toggle fullscreen"></button>

                <button class="pswp__button pswp__button--zoom" title="Zoom in/out"></button>

                <!-- Preloader demo http://codepen.io/dimsemenov/pen/yyBWoR -->
                <!-- element will get class pswp__preloader--active when preloader is running -->
                <div class="pswp__preloader">
                    <div class="pswp__preloader__icn">
                        <div class="pswp__preloader__cut">
                            <div class="pswp__preloader__donut"></div>
                        </div>
                    </div>
                </div>
            </div>

            <div class="pswp__share-modal pswp__share-modal--hidden pswp__single-tap">
                <div class="pswp__share-tooltip"></div>
            </div>

            <button class="pswp__button pswp__button--arrow--left" title="Previous (arrow left)">
            </button>

            <button class="pswp__button pswp__button--arrow--right" title="Next (arrow right)">
            </button>

            <div class="pswp__caption">
                <div class="pswp__caption__center"></div>
            </div>

        </div>

    </div>

</div>

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/default-skin/default-skin.min.css">
<script src="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe-ui-default.min.js"></script>

<script>
    function viewer_init() {
        let pswpElement = document.querySelectorAll('.pswp')[0];
        let $imgArr = document.querySelectorAll(('.article-entry img:not(.reward-img)'))

        $imgArr.forEach(($em, i) => {
            $em.onclick = () => {
                // slider展开状态
                // todo: 这样不好，后面改成状态
                if (document.querySelector('.left-col.show')) return
                let items = []
                $imgArr.forEach(($em2, i2) => {
                    let img = $em2.getAttribute('data-idx', i2)
                    let src = $em2.getAttribute('data-target') || $em2.getAttribute('src')
                    let title = $em2.getAttribute('alt')
                    // 获得原图尺寸
                    const image = new Image()
                    image.src = src
                    items.push({
                        src: src,
                        w: image.width || $em2.width,
                        h: image.height || $em2.height,
                        title: title
                    })
                })
                var gallery = new PhotoSwipe(pswpElement, PhotoSwipeUI_Default, items, {
                    index: parseInt(i)
                });
                gallery.init()
            }
        })
    }
    viewer_init()
</script>

<!-- MathJax -->

<script type="text/x-mathjax-config">
  MathJax.Hub.Config({
      tex2jax: {
          inlineMath: [ ['$','$'], ["\\(","\\)"]  ],
          processEscapes: true,
          skipTags: ['script', 'noscript', 'style', 'textarea', 'pre', 'code']
      }
  });

  MathJax.Hub.Queue(function() {
      var all = MathJax.Hub.getAllJax(), i;
      for(i=0; i < all.length; i += 1) {
          all[i].SourceElement().parentNode.className += ' has-jax';
      }
  });
</script>

<script src="https://cdn.jsdelivr.net/npm/mathjax@2.7.6/unpacked/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
<script>
  var ayerConfig = {
    mathjax: true
  }
</script>

<!-- Katex -->

<!-- busuanzi  -->


<script src="/js/busuanzi-2.3.pure.min.js"></script>


<!-- ClickLove -->


<script src="/js/clickLove.js"></script>


<!-- ClickBoom1 -->

<!-- ClickBoom2 -->

<!-- CodeCopy -->


<link rel="stylesheet" href="/css/clipboard.css">

<script src="https://cdn.jsdelivr.net/npm/clipboard@2/dist/clipboard.min.js"></script>
<script>
  function wait(callback, seconds) {
    var timelag = null;
    timelag = window.setTimeout(callback, seconds);
  }
  !function (e, t, a) {
    var initCopyCode = function(){
      var copyHtml = '';
      copyHtml += '<button class="btn-copy" data-clipboard-snippet="">';
      copyHtml += '<i class="ri-file-copy-2-line"></i><span>COPY</span>';
      copyHtml += '</button>';
      $(".highlight .code pre").before(copyHtml);
      $(".article pre code").before(copyHtml);
      var clipboard = new ClipboardJS('.btn-copy', {
        target: function(trigger) {
          return trigger.nextElementSibling;
        }
      });
      clipboard.on('success', function(e) {
        let $btn = $(e.trigger);
        $btn.addClass('copied');
        let $icon = $($btn.find('i'));
        $icon.removeClass('ri-file-copy-2-line');
        $icon.addClass('ri-checkbox-circle-line');
        let $span = $($btn.find('span'));
        $span[0].innerText = 'COPIED';
        
        wait(function () { // 等待两秒钟后恢复
          $icon.removeClass('ri-checkbox-circle-line');
          $icon.addClass('ri-file-copy-2-line');
          $span[0].innerText = 'COPY';
        }, 2000);
      });
      clipboard.on('error', function(e) {
        e.clearSelection();
        let $btn = $(e.trigger);
        $btn.addClass('copy-failed');
        let $icon = $($btn.find('i'));
        $icon.removeClass('ri-file-copy-2-line');
        $icon.addClass('ri-time-line');
        let $span = $($btn.find('span'));
        $span[0].innerText = 'COPY FAILED';
        
        wait(function () { // 等待两秒钟后恢复
          $icon.removeClass('ri-time-line');
          $icon.addClass('ri-file-copy-2-line');
          $span[0].innerText = 'COPY';
        }, 2000);
      });
    }
    initCopyCode();
  }(window, document);
</script>


<!-- CanvasBackground -->


<script src="/js/dz.js"></script>



    
  </div>
</body>

</html>